Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(sozo): upload world/models/contracts metadata only if changed #2691

Merged
merged 8 commits into from
Nov 28, 2024

Conversation

remybar
Copy link
Contributor

@remybar remybar commented Nov 14, 2024

Description

Closes #2096, #1907.

Tests

  • Yes
  • No, because they aren't needed
  • No, because I need help

Added to documentation?

Checklist

  • I've formatted my code (scripts/prettier.sh, scripts/rust_fmt.sh, scripts/cairo_fmt.sh)
  • I've linted my code (scripts/clippy.sh, scripts/docs.sh)
  • I've commented my code
  • I've requested a review after addressing the comments

Summary by CodeRabbit

Release Notes

  • New Features

    • Added support for IPFS configuration with new IpfsOptions and IpfsConfig structures.
    • Introduced IpfsService for uploading metadata to IPFS.
    • Enhanced ProfileConfig to manage multiple resource configurations for models, contracts, and events.
    • Added new fields metadata_hash to various structures for improved metadata handling.
  • Improvements

    • Updated migration processes to optionally include metadata uploads.
    • Enhanced event handling for MetadataUpdate events to reflect new metadata structures.
    • Expanded the WorldMetadata and ResourceMetadata structures to support additional metadata fields.
  • Tests

    • Expanded test coverage for metadata handling and IPFS interactions.
    • Introduced new tests for validating metadata uploads and configurations.
  • Bug Fixes

    • Improved error handling in metadata upload processes.

Copy link
Collaborator

@glihm glihm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Made a suggestion on from where we could fetch the current value.

It's a small change in the WorldRemote, but may be better integrated in the migration flow, wdyt?

crates/sozo/ops/src/migrate/mod.rs Outdated Show resolved Hide resolved
@glihm glihm changed the title feat: upload world/models/contracts metadata only if changed feat(sozo): upload world/models/contracts metadata only if changed Nov 14, 2024
@remybar remybar force-pushed the feat-metadata branch 3 times, most recently from 1a4b450 to f38113c Compare November 15, 2024 14:57
Copy link

codecov bot commented Nov 15, 2024

Codecov Report

Attention: Patch coverage is 80.32000% with 123 lines in your changes missing coverage. Please review.

Project coverage is 56.48%. Comparing base (ebcc23d) to head (c890b57).
Report is 2 commits behind head on main.

Files with missing lines Patch % Lines
bin/sozo/src/commands/migrate.rs 0.00% 31 Missing ⚠️
crates/dojo/world/src/services/ipfs_service.rs 0.00% 27 Missing ⚠️
crates/dojo/world/src/contracts/abigen/world.rs 20.00% 24 Missing ⚠️
crates/sozo/ops/src/migrate/mod.rs 82.08% 12 Missing ⚠️
crates/dojo/world/src/config/ipfs_config.rs 9.09% 10 Missing ⚠️
crates/dojo/world/src/uri.rs 55.55% 8 Missing ⚠️
bin/sozo/src/commands/auth.rs 33.33% 4 Missing ⚠️
crates/dojo/world/src/diff/resource.rs 66.66% 2 Missing ⚠️
crates/dojo/world/src/remote/resource.rs 86.66% 2 Missing ⚠️
bin/sozo/src/commands/dev.rs 0.00% 1 Missing ⚠️
... and 2 more
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2691      +/-   ##
==========================================
+ Coverage   56.24%   56.48%   +0.23%     
==========================================
  Files         415      421       +6     
  Lines       53241    53976     +735     
==========================================
+ Hits        29948    30488     +540     
- Misses      23293    23488     +195     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@remybar remybar force-pushed the feat-metadata branch 3 times, most recently from 4b304de to 091964a Compare November 17, 2024 16:28
@remybar remybar marked this pull request as ready for review November 17, 2024 16:28
Copy link

coderabbitai bot commented Nov 17, 2024

Walkthrough

Ohayo! This pull request introduces several enhancements primarily focused on metadata handling and IPFS integration. Key changes include the addition of IPFS-related options to the MigrateArgs struct, new metadata fields in various models, and improved upload functionalities for metadata to IPFS. Additionally, the UploadService trait is defined for managing uploads, and several new tests ensure the correctness of these features. The overall structure of the codebase is enhanced to support these functionalities without disrupting existing workflows.

Changes

File Path Change Summary
bin/sozo/src/commands/migrate.rs Enhanced MigrateArgs with new ipfs field; updated run method to handle IPFS metadata upload.
crates/dojo/core/src/model/metadata.cairo Added metadata_hash field to ResourceMetadata struct.
crates/dojo/core-cairo-test/src/tests/world/metadata.cairo Updated tests to include new metadata_hash field in assertions.
crates/dojo/world/src/config/metadata_config.rs Introduced ResourceMetadata struct; added Hash implementation for WorldMetadata and ResourceMetadata.
crates/dojo/world/src/config/profile_config.rs Added optional fields for models, contracts, and events in ProfileConfig.
crates/dojo/world/src/config/resource_config.rs Introduced new ResourceConfig struct for resource management.
crates/dojo/world/src/remote/events_to_remote.rs Updated match_event method to handle MetadataUpdate events.
crates/dojo/world/src/services/ipfs_service.rs Implemented IpfsService for uploading data to IPFS.
crates/dojo/world/src/services/upload_service.rs Defined UploadService trait for handling uploads.
crates/dojo/world/src/constants.rs Added new constant WORLD for resource identification.
crates/dojo/world/src/config/environment.rs Added ipfs_config field to Environment struct for IPFS settings.
bin/sozo/src/commands/options/ipfs.rs Introduced IpfsOptions struct for command-line argument management.
bin/sozo/src/commands/dev.rs Updated MigrateArgs to include ipfs field with default value.
bin/sozo/src/commands/auth.rs Replaced Felt::ZERO with WORLD for permissions management.
examples/spawn-and-move/dojo_dev.toml Added new models, events, and contracts; updated world_address.
crates/dojo/world/src/metadata/metadata_storage.rs Added MetadataStorage trait for metadata upload handling.
crates/dojo/world/src/metadata/mod.rs Added metadata_storage module and re-exported MetadataStorage.
bin/sozo/src/commands/options/mod.rs Added new module declaration for ipfs.
crates/dojo/utils/src/env.rs Introduced constants for IPFS configuration environment variables.
crates/dojo/world/src/services/mod.rs Added new modules for ipfs_service, mock_upload_service, and upload_service.
crates/dojo/world/src/services/mock_upload_service.rs Implemented MockUploadService for testing upload functionality.
crates/dojo/world/src/services/ipfs_service.rs Implemented IpfsService for uploading to IPFS and validating configurations.
crates/dojo/world/src/diff/compare.rs Updated comparison logic to include metadata_hash.
crates/dojo/world/src/diff/mod.rs Added metadata_hash field to WorldStatusInfo.
crates/dojo/world/src/diff/resource.rs Added metadata_hash method to ResourceDiff enum.
crates/dojo/world/src/metadata/metadata_test.rs Updated tests to validate new metadata handling functionalities.
scripts/rust_fmt.sh Enhanced formatting options for cargo fmt.
bin/sozo/tests/test_data/policies.json Updated target addresses and method names in policies.
crates/dojo/world/src/remote/mod.rs Added metadata_hash field to WorldRemote.
crates/dojo/world/src/remote/resource.rs Enhanced CommonRemoteInfo with metadata_hash.

Assessment against linked issues

Objective Addressed Explanation
Ensure metadata uploads occur after failures (#2096)
Implement easy updates for world metadata (#2096)

Possibly related PRs


🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 11

🧹 Outside diff range and nitpick comments (28)
crates/dojo/core/src/model/metadata.cairo (2)

12-12: Ohayo! The metadata_hash addition looks solid, sensei!

The new metadata_hash field is a well-thought-out solution that enables efficient change detection for metadata updates. This aligns perfectly with the PR's objective of uploading metadata only when necessary, especially after failed deployments.

This approach provides several benefits:

  • Efficient change detection through hash comparison
  • Prevents unnecessary uploads when metadata hasn't changed
  • Enables retry mechanisms after failed deployments

12-12: Consider adding documentation for the metadata_hash field, sensei!

While the implementation is solid, adding documentation comments would help other developers understand the purpose and usage of this new field.

 pub struct ResourceMetadata {
     #[key]
     pub resource_id: felt252,
     pub metadata_uri: ByteArray,
+    /// Hash of the metadata content, used to detect changes and prevent unnecessary uploads
     pub metadata_hash: felt252
 }
examples/spawn-and-move/dojo_dev.toml (2)

18-21: Consider enhancing the event documentation, sensei!

The event description could be more detailed to specify:

  • What data is emitted when a player moves
  • When exactly this event is triggered
  • Who can listen to this event

22-25: Please elaborate on the contract's actions, sensei!

The description "set of actions for a player" could be more specific about:

  • What actions are available
  • Any constraints or prerequisites for these actions
  • How these actions relate to the models and events
crates/dojo/world/src/metadata/ipfs.rs (1)

17-22: Consider adding retry mechanism for IPFS client initialization

The client initialization looks good, but IPFS services can be occasionally unstable. Consider adding retry logic for better reliability.

Example implementation:

use tokio::time::{sleep, Duration};

pub fn new() -> Result<Self> {
    let max_retries = 3;
    let mut attempt = 0;
    
    loop {
        match ipfs_api_backend_hyper::IpfsClient::from_str(IPFS_CLIENT_URL) {
            Ok(client) => return Ok(Self {
                client: client.with_credentials(IPFS_USERNAME, IPFS_PASSWORD),
            }),
            Err(e) if attempt < max_retries => {
                attempt += 1;
                sleep(Duration::from_secs(1)).await;
                continue;
            }
            Err(e) => return Err(e.into()),
        }
    }
}
crates/dojo/world/src/remote/mod.rs (1)

26-27: Consider adding tests for metadata_hash initialization and updates

Ohayo sensei! While the field addition is solid, we should ensure it's properly tested. Consider adding test cases to verify:

  1. Default initialization of metadata_hash
  2. Metadata hash updates through the WorldRemote interface

Here's a suggested test case:

#[test]
fn test_world_metadata_hash() {
    let world_remote = WorldRemote::default();
    assert_eq!(world_remote.metadata_hash, Felt::ZERO, "Default metadata hash should be zero");
}
crates/dojo/core-cairo-test/src/tests/world/metadata.cairo (1)

Line range hint 1-120: Consider adding edge cases for metadata_hash validation, sensei!

While the current test coverage is good, consider adding test cases for:

  1. Zero hash values
  2. Maximum u32 hash values
  3. Hash collisions between different metadata

Here's a suggested test case:

#[test]
fn test_set_metadata_edge_cases() {
    let world = deploy_world();
    let world = world.dispatcher;

    // Test zero hash
    let metadata_zero = ResourceMetadata {
        resource_id: 0,
        metadata_uri: format!("ipfs:zero"),
        metadata_hash: 0
    };
    world.set_metadata(metadata_zero.clone());
    assert(world.metadata(0) == metadata_zero, 'invalid zero hash');

    // Test max hash
    let metadata_max = ResourceMetadata {
        resource_id: 0,
        metadata_uri: format!("ipfs:max"),
        metadata_hash: 0xffffffff
    };
    world.set_metadata(metadata_max.clone());
    assert(world.metadata(0) == metadata_max, 'invalid max hash');
}
crates/dojo/world/src/uri.rs (2)

21-35: Excellent Hash implementation, sensei!

The Hash implementation is well-structured and correctly handles all Uri variants. This aligns perfectly with the PR's objective of tracking metadata changes.

Consider adding test cases for the Hash implementation to ensure consistent behavior across variants. Here's a suggested test:

#[test]
fn test_uri_hash() {
    use std::collections::hash_map::DefaultHasher;
    use std::hash::{Hash, Hasher};

    fn calculate_hash<T: Hash>(t: &T) -> u64 {
        let mut s = DefaultHasher::new();
        t.hash(&mut s);
        s.finish()
    }

    let uri1 = Uri::Ipfs("ipfs://QmYwAPJzv5CZsnA625s3Xf2nemtYgPpHdWEz79ojWnPbdG".to_string());
    let uri2 = Uri::Ipfs("ipfs://QmYwAPJzv5CZsnA625s3Xf2nemtYgPpHdWEz79ojWnPbdG".to_string());
    let uri3 = Uri::Ipfs("ipfs://different".to_string());

    assert_eq!(calculate_hash(&uri1), calculate_hash(&uri2));
    assert_ne!(calculate_hash(&uri1), calculate_hash(&uri3));
}

Line range hint 1-69: Ohayo! Great architectural contribution, sensei!

These implementations provide essential building blocks for the PR's objective of uploading metadata only when changed. The Hash implementation enables efficient change detection, while the Display implementation supports better debugging and logging capabilities.

This lays a solid foundation for metadata change tracking. Consider documenting these new capabilities in the module-level documentation to help other developers understand how to leverage them for metadata comparison.

crates/dojo/world/src/diff/resource.rs (2)

116-123: Ohayo! Add documentation for metadata hash semantics

The implementation looks solid, sensei! However, it would be helpful to document why Felt::ZERO is used for newly created resources and how this relates to the metadata upload process.

Consider adding documentation like this:

    /// Returns the current metadata hash of the resource.
+   ///
+   /// For newly created resources that haven't been deployed yet, returns `Felt::ZERO`
+   /// as no metadata has been uploaded. For existing resources, returns the hash of
+   /// their current metadata from the remote state.
    pub fn metadata_hash(&self) -> Felt {

116-123: Implementation aligns well with existing patterns

The implementation follows the established pattern of other methods in the enum, correctly handles all variants, and integrates smoothly with the remote metadata functionality. This change supports the PR objective of tracking metadata changes.

Consider adding a helper method to check if metadata needs updating by comparing the local metadata hash with this remote hash. This would support the PR's goal of uploading "only if changed".

crates/sozo/ops/src/tests/migration.rs (2)

89-111: Add documentation to the helper function, sensei!

While the function is well-structured, it would benefit from documentation explaining:

  • The purpose of the function
  • Parameter descriptions
  • The meaning of the checker function
  • Return value implications

Here's a suggested documentation block:

+/// Validates resource metadata in a WorldDiff against expected criteria
+/// 
+/// # Arguments
+/// * `diff` - The WorldDiff containing resources to check
+/// * `resources` - Optional vector of ResourceConfig to validate
+/// * `expected_count` - Expected number of resources
+/// * `checker` - Function to validate metadata hash values
+/// 
+/// # Panics
+/// Panics if assertions fail for resource count or metadata hash validation

113-150: Excellent test implementation, sensei!

The test thoroughly verifies metadata state transitions and covers all resource types. A few observations:

  1. Good use of helper functions is_set and is_not_set
  2. Comprehensive verification of world and resource metadata
  3. Clear comments explaining the test flow

One suggestion: Consider adding a test case for error conditions when metadata upload fails.

Here's a suggested additional test case:

#[tokio::test(flavor = "multi_thread")]
#[katana_runner::test(accounts = 10)]
async fn upload_metadata_handles_errors(sequencer: &RunnerCtx) {
    // Test implementation for error conditions
    // This would help verify the error handling in the migration process
}
crates/dojo/world/src/diff/compare.rs (1)

Line range hint 17-27: Ohayo! Consider adding metadata_hash comparison in ContractLocal implementation

The comparison logic only checks class_hash but doesn't account for metadata changes. Based on the PR objectives to handle metadata updates, we should also compare metadata_hash.

Here's a suggested implementation:

 impl ComparableResource for ContractLocal {
     fn compare(self, remote: ResourceRemote) -> ResourceDiff {
         let remote_contract = remote.as_contract_or_panic();
 
-        if self.common.class_hash == remote_contract.common.current_class_hash() {
+        if self.common.class_hash == remote_contract.common.current_class_hash() 
+            && self.metadata_hash == remote_contract.common.metadata_hash {
             ResourceDiff::Synced(ResourceLocal::Contract(self), remote)
         } else {
             ResourceDiff::Updated(ResourceLocal::Contract(self), remote)
         }
     }
 }
crates/dojo/world/src/config/profile_config.rs (2)

21-23: Ohayo! Well-structured metadata fields, sensei!

The new optional fields for models, contracts, and events are well-organized and follow a consistent pattern. This structure aligns perfectly with the PR's objective to improve metadata handling.

Consider documenting these fields with rustdoc comments to help future maintainers understand their purpose and usage patterns.


141-154: Ohayo! Consider adding edge cases to the test suite, sensei.

While the current test coverage is good, consider adding these scenarios:

  1. Empty vectors for models/contracts/events
  2. Missing optional fields (description, icon_uri)
  3. Invalid URI formats
  4. Duplicate tags within the same resource type

This will help ensure robust handling of various input scenarios.

Here's a suggested test case to add:

#[test]
fn test_profile_config_edge_cases() {
    let content = r#"
    [world]
    name = "test"
    seed = "abcd"

    [namespace]
    default = "test"

    [[models]]
    tag = "empty-model"

    [[contracts]]
    tag = "invalid-contract"
    icon_uri = "not-a-valid-uri"
    "#;

    let config = toml::from_str::<ProfileConfig>(content).unwrap();
    
    // Test empty models
    assert!(config.models.is_some());
    let models = config.models.unwrap();
    assert_eq!(models[0].description, None);
    assert_eq!(models[0].icon_uri, None);

    // Test invalid URI
    assert!(config.contracts.is_some());
    let contracts = config.contracts.unwrap();
    assert!(contracts[0].icon_uri.is_none());
}

Also applies to: 212-232

crates/dojo/world/src/diff/mod.rs (2)

82-82: Consider documenting the ZERO initialization significance

The initialization of metadata_hash to Felt::ZERO in from_local implies this is a new world without existing metadata, but this significance isn't documented.

-                metadata_hash: Felt::ZERO,
+                // Initialize to ZERO for new worlds without existing metadata
+                metadata_hash: Felt::ZERO,

Line range hint 32-123: Ohayo sensei! Clean and focused metadata tracking implementation

The approach of using a dedicated metadata_hash field in WorldStatusInfo is elegant and maintainable. It:

  1. Cleanly separates metadata state tracking from other world state
  2. Enables efficient metadata change detection
  3. Maintains backward compatibility with existing world diff logic

Consider documenting this design decision in the module-level documentation to help future maintainers understand the rationale.

crates/dojo/world/src/remote/events_to_remote.rs (2)

254-264: LGTM with a minor enhancement suggestion

Ohayo sensei! The MetadataUpdate event handling looks solid with proper separation between world and resource metadata updates.

Consider adding trace logging for world metadata updates similar to resource updates for consistency.

 if e.resource == Felt::ZERO {
     self.metadata_hash = e.hash;
+    trace!("World metadata updated with hash: {:#066x}", e.hash);
 } else {

532-563: Add test coverage for world metadata updates

Ohayo sensei! While the current test thoroughly covers resource metadata updates, we should also add test coverage for world metadata updates (when resource == Felt::ZERO).

#[tokio::test]
async fn test_world_metadata_updated_event() {
    let mut world_remote = WorldRemote::default();
    
    let event = WorldEvent::MetadataUpdate(world::MetadataUpdate {
        resource: Felt::ZERO,
        uri: ByteArray::from_string("ipfs://world").unwrap(),
        hash: Felt::THREE,
    });

    world_remote.match_event(event).unwrap();
    assert_eq!(world_remote.metadata_hash, Felt::THREE);
}
crates/dojo/core/src/world/world_contract.cairo (1)

114-115: Consider adding documentation for the hash field usage, sensei!

While the implementation is correct, it would be helpful to add documentation explaining:

  • The hash calculation method
  • When the hash changes
  • How to handle hash conflicts

Add documentation above the MetadataUpdate event:

+    /// Emitted when a resource's metadata is updated
+    /// * `resource` - The resource identifier
+    /// * `uri` - The metadata URI
+    /// * `hash` - Hash of the metadata content for change detection
     #[derive(Drop, starknet::Event)]
     pub struct MetadataUpdate {

Also applies to: 360-364

crates/dojo/world/src/metadata/mod.rs (3)

46-65: Refactor to eliminate code duplication between implementations

Ohayo, sensei! The upload methods in both WorldMetadata and ResourceMetadata share similar logic for handling metadata uploads. To adhere to the DRY (Don't Repeat Yourself) principle, consider refactoring the common code into a helper function or a generic implementation. This will enhance maintainability and readability.

Here's how you might refactor the code:

  1. Create a generic helper function:

    async fn upload_metadata<T>(meta: &mut T) -> Result<String>
    where
        T: Clone + Serialize + HasIconUri + HasCoverUri,
    {
        let ipfs_client = IpfsClient::new()?;
    
        if let Some(Uri::File(icon)) = meta.icon_uri() {
            let icon_data = std::fs::read(icon)?;
            meta.set_icon_uri(Some(Uri::Ipfs(ipfs_client.upload(icon_data).await?)));
        }
    
        if let Some(Uri::File(cover)) = meta.cover_uri() {
            let cover_data = std::fs::read(cover)?;
            meta.set_cover_uri(Some(Uri::Ipfs(ipfs_client.upload(cover_data).await?)));
        }
    
        let serialized = json!(meta).to_string();
        ipfs_client.upload(serialized).await
    }
  2. Define traits for accessing and modifying URIs:

    trait HasIconUri {
        fn icon_uri(&self) -> &Option<Uri>;
        fn set_icon_uri(&mut self, uri: Option<Uri>);
    }
    
    trait HasCoverUri {
        fn cover_uri(&self) -> &Option<Uri>;
        fn set_cover_uri(&mut self, uri: Option<Uri>);
    }
  3. Implement the traits for your metadata structs:

    impl HasIconUri for WorldMetadata {
        fn icon_uri(&self) -> &Option<Uri> {
            &self.icon_uri
        }
    
        fn set_icon_uri(&mut self, uri: Option<Uri>) {
            self.icon_uri = uri;
        }
    }
    
    impl HasCoverUri for WorldMetadata {
        fn cover_uri(&self) -> &Option<Uri> {
            &self.cover_uri
        }
    
        fn set_cover_uri(&mut self, uri: Option<Uri>) {
            self.cover_uri = uri;
        }
    }
    
    impl HasIconUri for ResourceMetadata {
        fn icon_uri(&self) -> &Option<Uri> {
            &self.icon_uri
        }
    
        fn set_icon_uri(&mut self, uri: Option<Uri>) {
            self.icon_uri = uri;
        }
    }
    
    // ResourceMetadata may not have a cover_uri; implement accordingly
  4. Update the upload methods:

    #[async_trait]
    impl MetadataStorage for WorldMetadata {
        async fn upload(&self) -> Result<String> {
            let mut meta = self.clone();
            upload_metadata(&mut meta).await
        }
    }
    
    #[async_trait]
    impl MetadataStorage for ResourceMetadata {
        async fn upload(&self) -> Result<String> {
            let mut meta = self.clone();
            upload_metadata(&mut meta).await
        }
    }

    Adjust the traits and helper functions based on the specific fields each metadata struct contains.

Also applies to: 67-81


48-48: Assess the impact of cloning large metadata structures

Ohayo, sensei! Cloning self to create meta can be costly if the metadata structures are large. Consider optimizing to clone only the necessary parts or borrow mutably if possible.

Example modification:

  • Instead of cloning the entire object, create a new instance with updated fields or refactor methods to work with references where appropriate.

Also applies to: 69-69


52-60: Handle potential errors when reading files and uploading to IPFS

Ohayo, sensei! When reading files and uploading data to IPFS, consider adding more robust error handling or user-friendly error messages to assist in debugging if file reading fails or upload issues occur.

Example:

  • Wrap file reading and uploading in a custom function that provides context-specific error messages.

Also applies to: 73-76

crates/dojo/world/src/metadata/metadata_test.rs (2)

21-23: Sensei, consider using crate-relative paths to ensure test data is found

Using relative paths like "./src/metadata/metadata_test_data/cover.png" may cause tests to fail if they're run from a different working directory. Ohayo! To make the tests more robust, you might use env!("CARGO_MANIFEST_DIR") to build paths relative to the crate root.

You can modify the path construction as follows:

-PathBuf::from_str("./src/metadata/metadata_test_data/cover.png").unwrap()
+PathBuf::from(format!(
+    "{}/src/metadata/metadata_test_data/cover.png",
+    env!("CARGO_MANIFEST_DIR")
+))

Also applies to: 45-47


137-137: Consider mocking IpfsClient for tests

Ohayo, sensei! The TODO comment suggests that it would be beneficial to fake or mock the IpfsClient for tests to avoid actual IPFS calls and improve test reliability and speed.

Would you like assistance in implementing a mock IpfsClient for your tests?

crates/sozo/ops/src/migrate/mod.rs (2)

172-189: Avoid unnecessary cloning of item

Ohayo, sensei! To improve efficiency, consider passing a reference to item instead of cloning it when creating new_metadata.

Apply this diff:

-        let new_metadata = metadata_config::ResourceMetadata::from(item.clone());
+        let new_metadata = metadata_config::ResourceMetadata::from(item);

154-155: Simplify error handling by avoiding string conversion

Ohayo, sensei! Instead of converting errors to strings and then wrapping them with anyhow!, you can use anyhow::Error::from to preserve error context.

Apply this diff:

-        invoker.multicall().await.map_err(|e| anyhow!(e.to_string()))?;
+        invoker.multicall().await.map_err(anyhow::Error::from)?;

And similarly later:

-        invoker.invoke_all_sequentially().await.map_err(|e| anyhow!(e.to_string()))?;
+        invoker.invoke_all_sequentially().await.map_err(anyhow::Error::from)?;

Also applies to: 160-161

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between f6659db and 091964a.

⛔ Files ignored due to path filters (4)
  • Cargo.lock is excluded by !**/*.lock
  • crates/dojo/world/src/metadata/metadata_test_data/icon.png is excluded by !**/*.png
  • spawn-and-move-db.tar.gz is excluded by !**/*.gz
  • types-test-db.tar.gz is excluded by !**/*.gz
📒 Files selected for processing (27)
  • bin/sozo/src/commands/migrate.rs (1 hunks)
  • crates/dojo/core-cairo-test/src/tests/world/metadata.cairo (6 hunks)
  • crates/dojo/core/src/model/metadata.cairo (1 hunks)
  • crates/dojo/core/src/world/world_contract.cairo (2 hunks)
  • crates/dojo/world/Cargo.toml (1 hunks)
  • crates/dojo/world/src/config/metadata_config.rs (2 hunks)
  • crates/dojo/world/src/config/mod.rs (1 hunks)
  • crates/dojo/world/src/config/profile_config.rs (4 hunks)
  • crates/dojo/world/src/config/resource_config.rs (1 hunks)
  • crates/dojo/world/src/contracts/abigen/world.rs (8 hunks)
  • crates/dojo/world/src/diff/compare.rs (3 hunks)
  • crates/dojo/world/src/diff/mod.rs (3 hunks)
  • crates/dojo/world/src/diff/resource.rs (1 hunks)
  • crates/dojo/world/src/metadata/ipfs.rs (1 hunks)
  • crates/dojo/world/src/metadata/metadata_test.rs (1 hunks)
  • crates/dojo/world/src/metadata/metadata_test_data/abi.json (0 hunks)
  • crates/dojo/world/src/metadata/metadata_test_data/source.cairo (0 hunks)
  • crates/dojo/world/src/metadata/mod.rs (1 hunks)
  • crates/dojo/world/src/metadata/world.rs (0 hunks)
  • crates/dojo/world/src/remote/events_to_remote.rs (3 hunks)
  • crates/dojo/world/src/remote/mod.rs (1 hunks)
  • crates/dojo/world/src/remote/resource.rs (3 hunks)
  • crates/dojo/world/src/uri.rs (3 hunks)
  • crates/sozo/ops/Cargo.toml (1 hunks)
  • crates/sozo/ops/src/migrate/mod.rs (2 hunks)
  • crates/sozo/ops/src/tests/migration.rs (4 hunks)
  • examples/spawn-and-move/dojo_dev.toml (2 hunks)
💤 Files with no reviewable changes (3)
  • crates/dojo/world/src/metadata/metadata_test_data/abi.json
  • crates/dojo/world/src/metadata/metadata_test_data/source.cairo
  • crates/dojo/world/src/metadata/world.rs
🔇 Additional comments (39)
crates/dojo/world/src/config/resource_config.rs (1)

1-4: Ohayo! Clean and minimal imports, looking good!

The imports are well-organized and include only what's necessary for the functionality.

crates/dojo/world/src/config/mod.rs (2)

7-7: Ohayo! Clean module organization, sensei!

The addition of the resource_config module and its public export follows the established pattern and supports the enhanced metadata handling capabilities.

Also applies to: 14-14


7-7: Let's verify the module integration, sensei!

Since this module is part of the metadata handling improvements, let's verify its integration with related modules.

Also applies to: 14-14

✅ Verification successful

Ohayo sensei! The ResourceConfig integration looks solid!

The verification shows that ResourceConfig is well-integrated across the codebase:

  • Used in profile_config.rs for models, contracts, and events configuration
  • Properly integrated with metadata handling through metadata_config.rs
  • Utilized in migration operations (sozo/ops)
  • Correctly exported in the module hierarchy
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the ResourceConfig usage across the codebase
# Expected: Find usage in profile_config.rs and metadata handling code

# Check for ResourceConfig usage
echo "Checking ResourceConfig usage..."
rg -p "ResourceConfig" --type rust

# Check for relationship with metadata handling
echo "Checking metadata-related code..."
rg -p "metadata" --type rust --glob "crates/dojo/world/src/**"

Length of output: 10333

examples/spawn-and-move/dojo_dev.toml (2)

34-34: Verify world address configuration, sensei!

The world address has been updated. Let's ensure this new address is properly configured for metadata handling.

#!/bin/bash
# Search for references to the world address
rg "0x52ee4d3cba58d1a0462bbfb6813bf5aa1b35078c3b859cded2b727c1d9469ea"

# Check for any world initialization or metadata upload scripts
fd -g "*world*.{sh,rs,cairo}" --exec grep -l "metadata"

6-17: Ohayo! Please provide schema details for the models, sensei!

While the models' descriptions are clear, it would be helpful to include the schema information (fields, types, etc.) for each model to ensure proper integration with the metadata handling system.

Let's check if schema information exists elsewhere:

crates/dojo/world/Cargo.toml (1)

33-33: LGTM! The futures dependency addition is well-aligned.

Ohayo! The addition of futures as a dev-dependency makes perfect sense for testing the new async metadata upload functionality, sensei! It complements the existing async-related dependencies like async-trait and tokio.

crates/sozo/ops/Cargo.toml (2)

16-16: Ohayo! The metadata feature addition looks good, sensei!

The addition of the metadata feature to dojo-world aligns perfectly with the PR objectives for improving metadata handling. This change enables the core functionality needed for the new metadata upload process.

Let's verify the feature usage:

✅ Verification successful

Ohayo! The metadata feature is properly utilized, sensei!

The verification shows that the metadata feature from dojo-world is actively used in the sozo crate for:

  • Uploading world and resource metadata to IPFS
  • Managing metadata hashes and URIs
  • Handling metadata storage and updates

The feature addition is necessary and correctly implemented, with clear usage in crates/sozo/ops/src/migrate/mod.rs for the metadata upload functionality.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify that the metadata feature is being used in the codebase

# Search for metadata-related functionality usage
rg -l "use.*dojo_world.*metadata" 

# Search for potential metadata feature gates
rg "#\[cfg\(feature.*metadata\)\]"

Length of output: 11950


Script:

#!/bin/bash
# Let's verify the metadata feature usage in dojo-world and sozo
# First, check for metadata-related imports and usage in sozo
rg -A 5 "use dojo_world::metadata" crates/sozo/

# Then check for metadata-related functionality in sozo
rg -A 5 "metadata" crates/sozo/ops/src/

Length of output: 13032


Line range hint 1-50: Verify impact of removing ipfs-api-backend-hyper, sensei

The removal of ipfs-api-backend-hyper from dev-dependencies might affect metadata-related tests. Let's ensure this doesn't impact the metadata upload functionality.

✅ Verification successful

The removal of ipfs-api-backend-hyper dependency is safe

The IPFS functionality in sozo-ops is handled through the dojo-world crate's metadata feature, which we can see is properly configured in the dependencies. The IPFS client dependency was only needed in dev-dependencies for testing, and those tests are now properly handled in dojo-world's metadata tests.

  • The only IPFS references in sozo-ops are:
    • A comment noting that IPFS testing is handled in dojo-world
    • A function doc comment describing metadata upload functionality
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for any remaining IPFS-related functionality that might be affected

# Search for IPFS-related imports or usage
rg -i "ipfs|ipfs_api"

# Look for any test files that might need the IPFS backend
fd -e rs test_ --exec rg -l "ipfs"

Length of output: 11950


Script:

#!/bin/bash
# Let's check if sozo-ops has any direct IPFS functionality or tests that might be affected

# Check for IPFS-related code in sozo-ops
rg -i "ipfs" crates/sozo/ops/ -A 5

# Check if sozo-ops has any dependencies on IPFS-related crates in its dependency tree
cargo tree -p sozo-ops | rg -i "ipfs"

# Check if there are any IPFS-related imports in sozo-ops
ast-grep --pattern 'use $_ :: $$$ipfs$$$'

Length of output: 1233

crates/dojo/world/src/metadata/ipfs.rs (2)

12-14: LGTM! Well-structured client wrapper

The struct provides good encapsulation of the underlying IPFS client implementation.


41-49: LGTM! Well-implemented test utility

The test-only get method is well-implemented with proper URI handling and error propagation.

crates/dojo/world/src/remote/mod.rs (2)

26-27: Ohayo! The metadata_hash field addition looks good, sensei!

The new field is well-documented and properly integrated into the struct. The use of Felt type is appropriate for Starknet compatibility.


26-27: Verify metadata_hash usage across the codebase

Ohayo sensei! Let's ensure the new field is properly utilized in related components.

✅ Verification successful

metadata_hash field is properly integrated across the codebase

Ohayo sensei! After reviewing the implementation, I can confirm that the metadata_hash field is well-integrated:

  • Properly initialized in resource creation (default: Felt::ZERO)
  • Has getter/setter methods in ResourceRemote
  • Correctly handled in MetadataUpdate events
  • Properly tracked in ResourceDiff for state management
  • Used in test assertions verifying the update functionality
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for metadata_hash usage in related files
# Expected: Find references in events_to_remote.rs and other related files

echo "Checking metadata_hash usage in related files..."
rg -l "metadata_hash" ./crates/dojo/world/src/

echo "Checking for potential metadata update handlers..."
ast-grep --pattern 'fn $name($$$) {
    $$$
    metadata_hash
    $$$
}'

Length of output: 678


Script:

#!/bin/bash
# Let's check the actual usage context of metadata_hash in these files

echo "Checking metadata_hash usage in remote/resource.rs..."
rg "metadata_hash" ./crates/dojo/world/src/remote/resource.rs -B 2 -A 2

echo -e "\nChecking metadata_hash usage in metadata/mod.rs..."
rg "metadata_hash" ./crates/dojo/world/src/metadata/mod.rs -B 2 -A 2

echo -e "\nChecking metadata_hash usage in remote/events_to_remote.rs..."
rg "metadata_hash" ./crates/dojo/world/src/remote/events_to_remote.rs -B 2 -A 2

echo -e "\nChecking metadata_hash usage in diff/resource.rs..."
rg "metadata_hash" ./crates/dojo/world/src/diff/resource.rs -B 2 -A 2

echo -e "\nChecking metadata_hash usage in diff/compare.rs..."
rg "metadata_hash" ./crates/dojo/world/src/diff/compare.rs -B 2 -A 2

Length of output: 4078

bin/sozo/src/commands/migrate.rs (1)

78-79: Ohayo! The metadata upload implementation looks solid, sensei! 🎋

The addition of metadata upload functionality after migration but before manifest writing is well-placed and properly handles errors. This change effectively addresses the issue where metadata wouldn't be uploaded after failed deployments.

However, let's verify the error recovery scenarios to ensure robustness.

✅ Verification successful

Let me gather more information about the metadata upload implementation and error handling.


Ohayo! The metadata upload implementation is well-protected, sensei! 🎋

The implementation shows robust error handling with proper context messages. The codebase includes:

  • Comprehensive test coverage for metadata upload in dojo/world/src/metadata/metadata_test.rs
  • Error recovery through the retry macro in saya/core/src/macros.rs with up to 20 retries
  • Proper error propagation with descriptive context messages

The metadata upload during migration is correctly placed and follows the established error handling patterns in the codebase.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Search for error handling patterns in metadata upload scenarios
# Expected: Find test cases covering metadata upload failures and recovery

# Look for test cases related to metadata upload failures
rg -A 10 "upload_metadata.*fail|fail.*upload_metadata" --type rust

# Look for error recovery scenarios in tests
ast-grep --pattern 'fn test_$_metadata_upload($_) {
  $$$
}'

Length of output: 1847


Script:

#!/bin/bash
# Search for metadata upload implementation and error handling
ast-grep --pattern 'impl $_ {
  $$$
  upload_metadata($$$) {
    $$$
  }
  $$$
}'

# Look for retry mechanisms or error recovery patterns
rg -A 5 "retry|recover|fallback" --type rust

# Find test cases that might cover metadata upload
rg -A 10 "test.*metadata|metadata.*test" --type rust

Length of output: 23182

crates/dojo/core-cairo-test/src/tests/world/metadata.cairo (3)

12-12: LGTM! The metadata initialization looks good, sensei!

The new metadata_hash field is properly initialized with a test value of 42.


74-74: Consistent metadata initialization across negative test cases!

The metadata_hash field is consistently initialized across all test cases, including permission checks and security scenarios. This ensures thorough validation of the new field under various error conditions.

Also applies to: 89-89, 116-116


Line range hint 33-49: Ohayo! The metadata event validation is thorough!

The test properly verifies all metadata fields, including the new metadata_hash. The event assertion at line 49 ensures the hash is correctly propagated through the system.

Let's verify the event structure matches the implementation:

✅ Verification successful

Ohayo! The metadata event validation is perfectly aligned with the contract implementation, sensei!

The test correctly validates all fields of the MetadataUpdate event:

  • resource field matches the contract's felt252 type
  • uri field matches the contract's ByteArray type
  • hash field matches the contract's felt252 type

The event structure in the world contract exactly matches what's being tested, including the order and types of all fields.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify MetadataUpdate event structure in the world contract
ast-grep --pattern 'enum Event {
  $$$
  MetadataUpdate: MetadataUpdate,
  $$$
}'

Length of output: 83


Script:

#!/bin/bash
# Let's try a different approach to find the event structure
rg -A 5 "struct MetadataUpdate" 

Length of output: 1451


Script:

#!/bin/bash
# Also search for the event enum definition with a more flexible pattern
rg -A 10 "enum Event \{"

Length of output: 28301


Script:

#!/bin/bash
# Search for files that might contain the event definition
fd "world\.cairo$"

Length of output: 212


Script:

#!/bin/bash
# Let's check the world contract for MetadataUpdate event
rg -A 5 "MetadataUpdate" crates/dojo/core/src/world/world_contract.cairo

Length of output: 705


Script:

#!/bin/bash
# Also check the event struct definition
rg -B 5 -A 5 "struct MetadataUpdate" crates/dojo/core/src/world/world_contract.cairo

Length of output: 334

crates/dojo/world/src/uri.rs (1)

1-1: Ohayo! Clean import addition, sensei!

The new import for Hash and Hasher traits is well-placed and necessary for the Hash implementation.

crates/dojo/world/src/diff/resource.rs (1)

116-123: Verify metadata upload integration

Let's verify that this implementation properly integrates with the metadata upload logic in other files.

✅ Verification successful

Implementation verified and properly integrated, sensei! ✨

The metadata_hash implementation in resource.rs is correctly integrated with the metadata upload system. The code shows:

  • In migrate/mod.rs, the hash is used to compare current and new metadata states before uploads
  • In remote/resource.rs, there's proper hash management with getters/setters
  • In metadata/mod.rs, there's hash computation support

The implementation aligns perfectly with the PR's goal of uploading metadata only when changed, as evidenced by the comparison logic in the migration module.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check for metadata upload implementation that would use this hash
echo "Searching for metadata upload implementation..."
rg -l "metadata_hash" --type rust

# Look for any tests involving metadata hash comparison
echo "Searching for related tests..."
rg -l "test.*metadata.*hash" --type rust

Length of output: 649


Script:

#!/bin/bash
# Let's check how metadata_hash is used in the implementation files
echo "Checking metadata_hash usage in remote/resource.rs..."
rg "metadata_hash" -B 2 -A 2 crates/dojo/world/src/remote/resource.rs

echo "\nChecking metadata_hash usage in metadata/mod.rs..."
rg "metadata_hash" -B 2 -A 2 crates/dojo/world/src/metadata/mod.rs

echo "\nChecking metadata_hash usage in migrate/mod.rs..."
rg "metadata_hash" -B 2 -A 2 crates/sozo/ops/src/migrate/mod.rs

# Let's also check for any metadata comparison logic
echo "\nChecking for metadata comparison implementations..."
ast-grep --pattern 'fn $FUNC_NAME($$$) {
    $$$
    metadata_hash
    $$$
}'

Length of output: 3101

crates/sozo/ops/src/tests/migration.rs (3)

8-8: Ohayo! The new imports look good, sensei!

The addition of ResourceConfig from dojo_world::config aligns well with the PR's metadata handling objectives.

Also applies to: 16-16


75-75: Excellent backward compatibility maintenance, sensei!

The existing tests have been properly updated to pass false for with_metadata, maintaining their original behavior while accommodating the new feature.

Also applies to: 84-87


42-42: Clean implementation of the metadata upload feature, sensei!

The addition of the with_metadata parameter and conditional upload logic aligns perfectly with the PR objectives. The error handling is robust with descriptive messages.

Let's verify the error handling behavior:

Also applies to: 63-69

✅ Verification successful

Let me analyze the error handling patterns in the migration-related code to verify the implementation.


Clean error handling implementation confirmed, sensei!

The migrate_spawn_and_move function properly propagates errors through the MigrationResult type, which is consistently used across the codebase. The error handling is robust with:

  • Proper error propagation using the ? operator
  • Structured result type containing both success status and manifest
  • Clear separation between metadata and migration concerns
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for other error handling patterns in migration-related code
rg -A 2 "expect|unwrap|context" crates/sozo/ops/src/

Length of output: 25177


Script:

#!/bin/bash
# Search for error handling in migration.rs and related files
rg -A 5 "migrate_spawn_and_move|MigrationResult" crates/sozo/ops/src/

Length of output: 3783

crates/dojo/world/src/diff/compare.rs (2)

Line range hint 29-39: Similar metadata comparison needed for Model and Event implementations, sensei

The ModelLocal and EventLocal implementations also need to be updated to handle metadata changes consistently.

Let's verify if there are any other implementations we might have missed:

Also applies to: 41-51


Line range hint 53-63: Review namespace metadata handling strategy

The NamespaceLocal comparison uses unreachable!() for updates. Should we consider allowing metadata updates for namespaces as well?

Let's check if namespaces have metadata in other parts of the codebase:

✅ Verification successful

Let's search for more context about namespace structures and metadata handling.


Ohayo sensei! After analyzing the codebase, I can now provide a definitive response about the namespace metadata concern.

The unreachable!() for namespace updates is correct and should be maintained

Looking at the struct definitions, we can see that NamespaceLocal only contains a name field, while NamespaceRemote has name, owners, and writers. The comparison logic is intentionally strict because:

  • Namespaces are fundamental identity containers that shouldn't change names after creation
  • Permission changes (owners/writers) are handled separately through dedicated flows
  • There is no additional metadata to sync between local and remote representations
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for namespace metadata references
rg -A 3 'namespace.*metadata' 

Length of output: 1296


Script:

#!/bin/bash
# Search for namespace struct definitions and implementations
ast-grep --pattern 'struct Namespace {
  $$$
}'

# Also check for NamespaceLocal struct
ast-grep --pattern 'struct NamespaceLocal {
  $$$
}'

# Look for any metadata fields in these structs
rg -B 2 -A 5 'struct (Namespace|NamespaceLocal)'

Length of output: 4690

crates/dojo/world/src/config/profile_config.rs (1)

12-12: Ohayo! Clean import addition.

The ResourceConfig import is well-placed and properly organized with other local imports.

crates/dojo/world/src/remote/resource.rs (2)

179-197: The new metadata hash methods look well-implemented, sensei!

The implementation follows the established patterns in the codebase and properly handles all resource types. The special handling of Namespace returning Felt::ZERO is consistent with similar methods in the file.

Let's verify the test coverage for these new methods:

#!/bin/bash
# Description: Check for test coverage of the new metadata hash methods
# Look for test files that exercise these methods

# Search for test files that might contain metadata hash related tests
echo "=== Checking for metadata hash tests ==="
rg -l "metadata_hash" "$(fd -t f -e rs test)"

# Look for specific test cases
echo "=== Checking for specific test cases ==="
rg "set_metadata_hash|metadata_hash" "$(fd -t f -e rs test)"

33-34: Ohayo! The metadata_hash field implementation looks solid, sensei!

The addition of metadata_hash with proper initialization to Felt::ZERO aligns well with the PR objectives for tracking metadata changes. Let's verify that all constructors properly initialize this field.

Also applies to: 85-85

crates/dojo/world/src/diff/mod.rs (2)

32-33: Ohayo! The metadata_hash field addition aligns with PR objectives

The addition of metadata_hash to WorldStatusInfo enables tracking metadata changes between local and remote worlds, which directly supports the PR's goal of selective metadata uploads.


123-123: Verify metadata hash propagation in remote world

The code correctly propagates the remote world's metadata hash, but let's verify there are no edge cases where this hash might be uninitialized or invalid.

✅ Verification successful

Let me gather more information about the metadata hash initialization and usage.


Metadata hash handling is properly implemented, sensei! ✨

The code analysis shows that metadata_hash is handled safely throughout the codebase:

  • It's properly propagated through WorldRemote for Contract, Model, and Event resources
  • Namespace resources explicitly return Felt::ZERO as their metadata hash
  • Tests verify the metadata hash updates and propagation
  • The hash is properly serialized/deserialized in the contract ABIs

The current implementation in the diff module correctly uses the remote world's metadata hash, which is consistent with the rest of the codebase's handling.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for potential uninitialized metadata hash cases
# Look for places where WorldRemote is constructed without setting metadata_hash

# Search for WorldRemote struct construction
ast-grep --pattern 'WorldRemote {
  $$$
  metadata_hash: $_,
  $$$
}'

# Search for potential metadata_hash assignments
rg -A 3 'metadata_hash\s*=' 

Length of output: 1523


Script:

#!/bin/bash
# Check for WorldRemote struct definition and initialization
ast-grep --pattern 'struct WorldRemote {
  $$$
}'

# Look for metadata_hash field initialization in constructors/new functions
ast-grep --pattern 'fn new($$$) -> WorldRemote {
  $$$
}'

# Search for any direct metadata_hash field access
rg -A 2 '\.metadata_hash'

Length of output: 4997

crates/dojo/world/src/remote/events_to_remote.rs (1)

58-58: LGTM! Event selector properly added

Ohayo! The MetadataUpdate event selector is correctly added to enable filtering of metadata update events.

crates/dojo/core/src/world/world_contract.cairo (2)

114-115: Ohayo! The MetadataUpdate event structure enhancement looks good, sensei!

The addition of the hash field enables efficient tracking of metadata changes, which directly addresses the PR objectives for uploading metadata only when changed.


360-364: The MetadataUpdate event emission is properly implemented!

The event emission correctly includes all fields including the new hash field, maintaining consistency with the event structure.

crates/dojo/world/src/contracts/abigen/world.rs (4)

Line range hint 394-428: Ohayo! The MetadataUpdate struct changes look good, sensei!

The addition of the hash field to track metadata changes is well implemented with proper serialization and deserialization support. This change enables tracking when metadata actually changes, which aligns with the PR objective.


Line range hint 632-665: The ResourceMetadata struct changes are solid, sensei!

The addition of the metadata_hash field provides a way to track the state of metadata, complementing the MetadataUpdate changes. The serialization and deserialization implementations are correct.


1804-1815: Event handling updates are properly implemented, sensei!

The MetadataUpdate event deserialization has been correctly updated to handle the new hash field in both TryFrom trait implementations. The error handling and deserialization logic are consistent.

Also applies to: 2684-2695


394-396: Verify consistent usage of hash fields across the codebase

Let's ensure the new hash fields are used consistently in all metadata-related operations.

Also applies to: 632-633

✅ Verification successful

Based on the search results, I can see that the hash field is consistently used across the codebase:

  1. The ResourceMetadata struct in world.rs properly includes the metadata_hash field
  2. The metadata hash is properly handled in metadata updates through MetadataUpdate events
  3. The hash field is consistently used in remote resources tracking via metadata_hash field
  4. There are proper test cases verifying metadata hash updates
  5. The hash field is properly integrated with the world state diff tracking

The hash field implementation is consistent across the codebase

The changes are properly integrated with all metadata-related operations including:

  • Resource metadata storage
  • Metadata updates and events
  • Remote state tracking
  • World state diffing
  • Test coverage
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for consistent usage of metadata hash fields
# Expected: All metadata-related code should handle the hash fields

# Search for metadata-related structs and their usage
rg -A 5 "struct.*Metadata|fn.*metadata" --type rust

# Search for hash field usage in metadata context
rg -A 3 "metadata.*hash|hash.*metadata" --type rust

Length of output: 42698

crates/dojo/world/src/metadata/mod.rs (1)

34-35: Ensure Felt conversion is correct and handles large hashes

Ohayo, sensei! When converting new_hash (a u64) into a Felt using Felt::from_raw([0, 0, 0, new_hash]), ensure that the hash value fits within the field and that the ordering of the array elements correctly represents the intended value.

Let's verify if the conversion aligns with the expected behavior. Consider running a test to confirm that the Felt value accurately represents the u64 hash.

crates/dojo/world/src/metadata/metadata_test.rs (1)

1-192: Ohayo, sensei! The tests are well-structured and comprehensive

The implementation of test_world_metadata and test_resource_metadata effectively verifies the upload and retrieval of metadata. The use of helper functions like build_world_metadata and assert_ipfs_content enhances readability and maintainability.

crates/sozo/ops/src/migrate/mod.rs (2)

109-164: Function upload_metadata is well-implemented

Ohayo, sensei! The new upload_metadata method properly handles metadata uploads for the world, contracts, models, and events. The integration with the invoker pattern and conditional multicall execution is efficient.


121-121: Verify the correctness of using Felt::ZERO for the world resource ID

Ohayo, sensei! Ensure that Felt::ZERO is indeed the correct identifier for the world resource when retrieving and updating metadata. Verifying this helps prevent potential mismatches or errors.

crates/dojo/world/src/config/resource_config.rs Outdated Show resolved Hide resolved
crates/dojo/world/Cargo.toml Show resolved Hide resolved
crates/dojo/world/src/metadata/ipfs.rs Outdated Show resolved Hide resolved
crates/dojo/world/src/metadata/ipfs.rs Outdated Show resolved Hide resolved
crates/dojo/world/src/uri.rs Show resolved Hide resolved
crates/dojo/world/src/metadata/mod.rs Outdated Show resolved Hide resolved
crates/sozo/ops/src/migrate/mod.rs Outdated Show resolved Hide resolved
remybar added a commit to remybar/book that referenced this pull request Nov 17, 2024
Update the Metadata page according to dojoengine/dojo#2691.
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Outside diff range and nitpick comments (12)
crates/dojo/world/src/metadata/mod.rs (1)

27-45: Enhance error handling with specific error types

Ohayo! While using anyhow::Result is convenient, it might be better to define specific error types for different failure scenarios:

  • IPFS upload failures
  • File reading errors
  • Hash computation errors

Consider creating a custom error enum:

#[derive(Debug, thiserror::Error)]
pub enum MetadataError {
    #[error("Failed to upload to IPFS: {0}")]
    IpfsUpload(#[from] ipfs::Error),
    #[error("Failed to read file: {0}")]
    FileRead(#[from] std::io::Error),
    #[error("Hash computation failed: {0}")]
    HashComputation(String),
}

pub type Result<T> = std::result::Result<T, MetadataError>;
crates/dojo/world/src/config/metadata_config.rs (1)

39-96: Ohayo, sensei! Consider implementing a common trait for file hashing

Since both WorldMetadata and ResourceMetadata share similar file hashing logic, consider extracting this functionality into a common trait to promote code reuse and ensure consistent error handling across all implementations.

Example trait design:

trait FileHasher {
    fn hash_file<H: Hasher>(&self, path: &std::path::Path, state: &mut H) {
        if let Ok(data) = std::fs::read(path) {
            data.hash(state);
        } else {
            eprintln!("Warning: Unable to read file for hashing: {}", path.display());
        }
    }
}
crates/sozo/ops/src/tests/migration.rs (3)

42-42: Consider enhancing error handling in metadata upload.

Ohayo sensei! While the implementation looks good, the error messages could be more descriptive to help with debugging.

Consider this enhancement:

-    let res = migration.migrate(&mut ui).await.expect("Migration spawn-and-move failed.");
+    let res = migration.migrate(&mut ui).await
+        .context("Failed to migrate spawn-and-move world")?;

     if with_metadata {
-        migration.upload_metadata(&mut ui).await.expect("Upload metadata failed");
+        migration.upload_metadata(&mut ui).await
+            .context("Failed to upload world metadata after successful migration")?;
     }

Also applies to: 63-69


89-111: Add documentation for the check_resources helper function.

Ohayo! The helper function is well-implemented, but it would benefit from documentation explaining:

  • Purpose and usage
  • Parameter descriptions
  • Expected behavior of the checker function

Consider adding this documentation:

+/// Validates metadata for a list of resources against expected criteria
+/// 
+/// # Arguments
+/// * `diff` - The WorldDiff containing the resources to check
+/// * `resources` - Optional list of ResourceConfig to validate
+/// * `expected_count` - Expected number of resources
+/// * `checker` - Function to validate the metadata hash of each resource
 fn check_resources(
     diff: &WorldDiff,
     resources: Option<Vec<ResourceConfig>>,
     expected_count: usize,
     checker: &dyn Fn(Felt) -> bool,
 ) {

113-150: Test implementation is thorough but could be more explicit.

Ohayo sensei! The test covers the key scenarios well, but consider these enhancements:

  1. Add explicit assertions for specific metadata values, not just presence/absence
  2. Test error cases (e.g., failed uploads)

Also, the test aligns perfectly with the PR objectives mentioned in #2096, ensuring metadata can be uploaded after initial deployment.

crates/dojo/world/src/metadata/metadata_test.rs (2)

14-64: Ohayo! Consider improving test data handling, sensei.

The helper functions could be more robust with these improvements:

  1. Extract hardcoded file paths into constants
  2. Add validation for social media URLs
  3. Consider using a test fixture or configuration file for test data

Here's a suggested improvement:

+ const TEST_DATA_DIR: &str = "./src/metadata/metadata_test_data";
+ const TEST_COVER_PATH: &str = "./src/metadata/metadata_test_data/cover.png";
+ const TEST_ICON_PATH: &str = "./src/metadata/metadata_test_data/icon.png";

fn build_world_metadata() -> WorldMetadata {
-    cover_uri: Some(Uri::File(
-        fs::canonicalize(
-            PathBuf::from_str("./src/metadata/metadata_test_data/cover.png").unwrap(),
-        )
-        .unwrap(),
-    )),
+    cover_uri: Some(Uri::File(
+        fs::canonicalize(PathBuf::from_str(TEST_COVER_PATH).unwrap())
+            .expect("Test cover image not found")
+    )),

66-138: Ohayo! Great test coverage, but let's make it even better, sensei!

The test thoroughly validates the happy path, but consider adding:

  1. Negative test cases (invalid URIs, network failures)
  2. Edge cases (empty fields, very large metadata)

Regarding the TODO on line 137:

Would you like me to help implement a mock IpfsClient for more reliable testing? This would:

  • Remove external IPFS dependency
  • Allow testing network failure scenarios
  • Speed up test execution
crates/dojo/world/src/diff/compare.rs (1)

Line range hint 19-29: Ohayo! Critical issue: Metadata changes are not detected in comparison logic

The comparison implementations for ContractLocal, ModelLocal, and EventLocal only check class_hash but ignore metadata_hash changes. This doesn't align with the PR's objective of properly handling metadata updates.

Here's how to fix the ContractLocal implementation (apply similar changes to ModelLocal and EventLocal):

 impl ComparableResource for ContractLocal {
     fn compare(self, remote: ResourceRemote) -> ResourceDiff {
         let remote_contract = remote.as_contract_or_panic();
 
-        if self.common.class_hash == remote_contract.common.current_class_hash() {
+        if self.common.class_hash == remote_contract.common.current_class_hash() 
+            && self.common.metadata_hash == remote_contract.common.metadata_hash {
             ResourceDiff::Synced(ResourceLocal::Contract(self), remote)
         } else {
             ResourceDiff::Updated(ResourceLocal::Contract(self), remote)
         }
     }
 }

Also applies to: 30-40, 41-51

crates/dojo/world/src/config/profile_config.rs (2)

21-23: Consider adding field-level documentation, sensei!

While the struct is well-documented, adding field-level documentation for the new metadata fields would help users understand their purpose and usage better.

Example addition:

 #[derive(Debug, Clone, Default, Deserialize)]
 pub struct ProfileConfig {
     pub world: WorldConfig,
+    /// Metadata configurations for models in the world
     pub models: Option<Vec<ResourceConfig>>,
+    /// Metadata configurations for contracts in the world
     pub contracts: Option<Vec<ResourceConfig>>,
+    /// Metadata configurations for events in the world
     pub events: Option<Vec<ResourceConfig>>,

212-231: Consider adding edge case tests, sensei!

While the happy path is well tested, consider adding tests for:

  • Empty vectors for models/contracts/events
  • Invalid URI formats
  • Duplicate tags within the same resource type

Example test case:

#[test]
fn test_profile_config_empty_resources() {
    let content = r#"
    [world]
    name = "test"
    seed = "abcd"

    [namespace]
    default = "test"

    [[models]]
    [[contracts]]
    [[events]]
    "#;

    let config = toml::from_str::<ProfileConfig>(content).unwrap();
    assert!(config.models.unwrap().is_empty());
    assert!(config.contracts.unwrap().is_empty());
    assert!(config.events.unwrap().is_empty());
}
crates/sozo/ops/src/migrate/mod.rs (2)

109-164: Ohayo, sensei! Ensure calls exist before invoking

To prevent unnecessary operations when there are no metadata changes, consider checking if there are any invocations before performing multicall or sequential calls. This enhances efficiency and avoids redundant actions.

Apply this diff to add the check:

     if !invoker.calls.is_empty() {
+        if self.do_multicall() {
             ui.update_text_boxed(format!("Uploading {} metadata...", invoker.calls.len()));
             invoker.multicall().await.map_err(|e| anyhow!(e.to_string()))?;
         } else {
             ui.update_text_boxed(format!(
                 "Uploading {} metadata (sequentially)...",
                 invoker.calls.len()
             ));
             invoker.invoke_all_sequentially().await.map_err(|e| anyhow!(e.to_string()))?;
         }
+    } else {
+        ui.update_text("No metadata changes detected. Skipping upload.");
+    }

166-192: Ohayo, sensei! Parallelize metadata uploads for better performance

The metadata uploads in upload_metadata_from_resource_config are currently processed sequentially. To optimize performance, especially with numerous resources, consider parallelizing the uploads using asynchronous tasks.

You can refactor the function to process uploads concurrently:

async fn upload_metadata_from_resource_config(
    &self,
    config: &[ResourceConfig],
) -> anyhow::Result<Vec<Call>> {
    let futures = config.iter().map(|item| async {
        let selector = dojo_types::naming::compute_selector_from_tag_or_name(&item.tag);
        let current_hash = self
            .diff
            .resources
            .get(&selector)
            .map_or(Felt::ZERO, |r| r.metadata_hash());
        let new_metadata = metadata_config::ResourceMetadata::from(item.clone());
        let res = new_metadata.upload_if_changed(current_hash).await?;

        if let Some((new_uri, new_hash)) = res {
            Ok(Some(self.world.set_metadata_getcall(&ResourceMetadata {
                resource_id: selector,
                metadata_uri: ByteArray::from_string(&new_uri)?,
                metadata_hash: new_hash,
            })))
        } else {
            Ok(None)
        }
    });

    let results = futures::future::join_all(futures).await;
    let mut calls = Vec::new();

    for result in results {
        if let Ok(Some(call)) = result {
            calls.push(call);
        }
    }

    Ok(calls)
}
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 091964a and e8a1bac.

⛔ Files ignored due to path filters (4)
  • Cargo.lock is excluded by !**/*.lock
  • crates/dojo/world/src/metadata/metadata_test_data/icon.png is excluded by !**/*.png
  • spawn-and-move-db.tar.gz is excluded by !**/*.gz
  • types-test-db.tar.gz is excluded by !**/*.gz
📒 Files selected for processing (27)
  • bin/sozo/src/commands/migrate.rs (1 hunks)
  • crates/dojo/core-cairo-test/src/tests/world/metadata.cairo (6 hunks)
  • crates/dojo/core/src/model/metadata.cairo (1 hunks)
  • crates/dojo/core/src/world/world_contract.cairo (2 hunks)
  • crates/dojo/world/Cargo.toml (1 hunks)
  • crates/dojo/world/src/config/metadata_config.rs (2 hunks)
  • crates/dojo/world/src/config/mod.rs (1 hunks)
  • crates/dojo/world/src/config/profile_config.rs (4 hunks)
  • crates/dojo/world/src/config/resource_config.rs (1 hunks)
  • crates/dojo/world/src/contracts/abigen/world.rs (8 hunks)
  • crates/dojo/world/src/diff/compare.rs (3 hunks)
  • crates/dojo/world/src/diff/mod.rs (3 hunks)
  • crates/dojo/world/src/diff/resource.rs (1 hunks)
  • crates/dojo/world/src/metadata/ipfs.rs (1 hunks)
  • crates/dojo/world/src/metadata/metadata_test.rs (1 hunks)
  • crates/dojo/world/src/metadata/metadata_test_data/abi.json (0 hunks)
  • crates/dojo/world/src/metadata/metadata_test_data/source.cairo (0 hunks)
  • crates/dojo/world/src/metadata/mod.rs (1 hunks)
  • crates/dojo/world/src/metadata/world.rs (0 hunks)
  • crates/dojo/world/src/remote/events_to_remote.rs (3 hunks)
  • crates/dojo/world/src/remote/mod.rs (1 hunks)
  • crates/dojo/world/src/remote/resource.rs (3 hunks)
  • crates/dojo/world/src/uri.rs (3 hunks)
  • crates/sozo/ops/Cargo.toml (1 hunks)
  • crates/sozo/ops/src/migrate/mod.rs (2 hunks)
  • crates/sozo/ops/src/tests/migration.rs (4 hunks)
  • examples/spawn-and-move/dojo_dev.toml (2 hunks)
💤 Files with no reviewable changes (3)
  • crates/dojo/world/src/metadata/metadata_test_data/abi.json
  • crates/dojo/world/src/metadata/metadata_test_data/source.cairo
  • crates/dojo/world/src/metadata/world.rs
🚧 Files skipped from review as they are similar to previous changes (15)
  • bin/sozo/src/commands/migrate.rs
  • crates/dojo/core-cairo-test/src/tests/world/metadata.cairo
  • crates/dojo/core/src/model/metadata.cairo
  • crates/dojo/core/src/world/world_contract.cairo
  • crates/dojo/world/Cargo.toml
  • crates/dojo/world/src/config/mod.rs
  • crates/dojo/world/src/config/resource_config.rs
  • crates/dojo/world/src/diff/mod.rs
  • crates/dojo/world/src/diff/resource.rs
  • crates/dojo/world/src/metadata/ipfs.rs
  • crates/dojo/world/src/remote/events_to_remote.rs
  • crates/dojo/world/src/remote/mod.rs
  • crates/dojo/world/src/remote/resource.rs
  • crates/sozo/ops/Cargo.toml
  • examples/spawn-and-move/dojo_dev.toml
🔇 Additional comments (20)
crates/dojo/world/src/metadata/mod.rs (1)

69-84: 🛠️ Refactor suggestion

Extract common functionality and maintain consistency

Ohayo! There's significant code duplication between WorldMetadata and ResourceMetadata implementations. Also, ResourceMetadata handles only icon_uri while WorldMetadata handles both icon and cover.

Consider extracting common functionality:

async fn upload_file_to_ipfs(
    file_path: &Path,
    ipfs_client: &IpfsClient
) -> Result<String> {
    if !file_path.exists() {
        return Err(anyhow!("File not found: {}", file_path.display()));
    }
    let data = std::fs::read(file_path)?;
    let uri = ipfs_client.upload(data).await?;
    // Verify upload
    let verified_data = ipfs_client.cat(&uri).await?;
    if verified_data != data {
        return Err(anyhow!("Upload verification failed"));
    }
    Ok(uri)
}

#[async_trait]
impl MetadataStorage for ResourceMetadata {
    async fn upload(&self, ipfs_client: &IpfsClient) -> Result<String> {
        let mut meta = self.clone();

        if let Some(Uri::File(icon)) = &self.icon_uri {
            meta.icon_uri = Some(Uri::Ipfs(upload_file_to_ipfs(icon, ipfs_client).await?));
        };

        let serialized = json!(meta).to_string();
        ipfs_client.upload(serialized).await
    }
}

Let's verify if ResourceMetadata should handle cover_uri:

crates/dojo/world/src/config/metadata_config.rs (4)

48-48: Ohayo, sensei! Previous review comment about HashMap hashing is still valid

The current implementation of hashing the socials HashMap could produce inconsistent hashes due to non-deterministic key ordering. Please refer to the previous review comment for the solution that involves sorting keys before hashing.


52-60: Ohayo, sensei! Previous review comment about error handling is still valid

The use of expect() for file operations can cause panics. Please refer to the previous review comment for a more graceful error handling solution.


64-81: Ohayo, sensei! Clean implementation of ResourceMetadata

The struct and its From implementation are well-designed with clear documentation and appropriate use of the naming utility.


91-94: Ohayo, sensei! Previous review comment about error handling applies here too

The same file reading error handling issue exists in the ResourceMetadata Hash implementation. Please refer to the previous review comment for the graceful error handling solution.

crates/dojo/world/src/uri.rs (2)

60-68: Ohayo! Clean Display implementation!

The Display implementation is well-crafted:

  • Safe handling of non-UTF8 paths using to_string_lossy()
  • Efficient direct formatting for URLs and IPFS URIs

Line range hint 21-68: Verify integration with metadata handling

Let's ensure the new Hash and Display implementations align with the metadata upload changes mentioned in the PR objectives.

✅ Verification successful

Ohayo sensei! After reviewing the implementation, I can now provide a definitive response about the Hash implementation for Uri.

Hash implementation is correctly aligned with serialization and metadata handling

The Hash implementation for Uri is consistent with its usage in metadata handling and follows best practices:

  1. The Hash implementation correctly handles all Uri variants (Http, Ipfs, File) by hashing their string representations
  2. This aligns with the serialization implementation which uses the same string representations
  3. The hashing is used properly in metadata handling through compute_metadata_hash function
  4. The implementation is used consistently across the codebase for resource metadata tracking
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify usage of Uri hashing in metadata-related code

# Search for metadata structs using Uri
rg --type rust "struct.*Metadata.*Uri" -C 5

# Search for metadata upload functions using Uri
rg --type rust "upload.*metadata.*Uri" -C 5

# Search for Hash derivations on metadata structs
ast-grep --pattern 'derive($$$, Hash, $$$)
struct $_Metadata'

Length of output: 155


Script:

#!/bin/bash
# Let's search for Uri usage in the codebase more broadly

# Find Uri struct definition and its usages
rg --type rust "enum Uri" -B 2 -A 5

# Search for metadata-related code that might use Uri
rg --type rust "Metadata" -B 2 -A 5

# Look for Uri serialization/deserialization usage
rg --type rust "Uri::from_string|Uri::serialize" -B 2 -A 5

# Search for tests involving Uri
rg --type rust "test.*Uri" -B 2 -A 5

Length of output: 112875


Script:

#!/bin/bash
# Let's search for the Uri implementation and its usage in metadata handling

# Find Uri implementation and its methods
rg --type rust "impl.*Uri" -B 2 -A 10

# Search for Uri usage in metadata-related code
rg --type rust "Uri.*metadata" -B 2 -A 5

# Look for Uri serialization tests
rg --type rust "test.*Uri.*serialize" -B 2 -A 5

# Search for metadata hash implementations
rg --type rust "metadata_hash" -B 2 -A 5

Length of output: 22749

crates/sozo/ops/src/tests/migration.rs (1)

8-8: LGTM! New imports align with metadata handling requirements.

Ohayo! The added imports for ResourceConfig and Felt are well-chosen for handling metadata and hash comparisons.

Also applies to: 16-16

crates/dojo/world/src/metadata/metadata_test.rs (1)

1-192: Ohayo! Overall excellent test coverage for the metadata changes, sensei!

The tests effectively validate the new metadata upload behavior, ensuring that:

  1. Metadata is uploaded only when changed
  2. Hashes are correctly tracked
  3. IPFS storage and retrieval work as expected

This aligns perfectly with the PR objectives to fix issues #2096 and #1907.

crates/dojo/world/src/diff/compare.rs (2)

111-111: Enhance test coverage for metadata changes, sensei!

The existing comment from the previous review is still valid. The tests include metadata_hash but don't verify its impact on comparison results. We need test cases that specifically verify metadata-only changes:

  1. Different metadata_hash but same class_hash
  2. Different metadata_hash and different class_hash
  3. Same metadata_hash but different class_hash

Also applies to: 147-147, 197-197


Line range hint 1-215: Verify metadata_hash field availability

Let's ensure the metadata_hash field exists in the required structures.

crates/dojo/world/src/config/profile_config.rs (3)

12-12: Ohayo! Import looks good, sensei!

The ResourceConfig import is properly organized with related configuration imports.


141-154: Test data structure looks good, sensei!

The TOML configuration test data is well-structured and covers the new metadata fields appropriately.


21-23: Verify metadata handling behavior, sensei!

The new fields enable better metadata management, aligning with PR objectives. Let's verify the metadata handling implementation:

✅ Verification successful

Based on the search results, I can see comprehensive metadata handling implementation across the codebase, particularly in the world configuration and resource management. The code shows proper metadata structures and upload functionality. Let me verify one more aspect about the actual metadata upload implementation:


Metadata implementation looks solid, sensei! ✨

The codebase shows robust metadata handling with:

  • Complete metadata structures for world and resources
  • Proper upload functionality with IPFS integration
  • Comprehensive test coverage including world metadata, resource metadata, and event handling
  • Consistent metadata storage and retrieval across the system

The new fields align perfectly with the existing metadata architecture and follow established patterns for resource configuration.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for consistent metadata handling across the codebase

# Look for metadata-related functions that should handle these new fields
rg -A 5 "metadata" --type rust

# Check for any existing metadata upload implementations
ast-grep --pattern 'fn $upload_metadata($$$) {
  $$$
}'

Length of output: 152061


Script:

#!/bin/bash
# Check for metadata upload implementation in world config
ast-grep --pattern 'impl $_ {
  $$$
  pub async fn upload($$$) {
    $$$
  }
  $$$
}'

# Look for metadata storage/handling tests
rg "test.*metadata" --type rust

Length of output: 590

crates/dojo/world/src/contracts/abigen/world.rs (4)

396-396: Ohayo! Nice addition of the hash field for metadata change tracking.

The new hash field in the MetadataUpdate struct enables efficient tracking of metadata changes, which aligns perfectly with the PR's objective of uploading metadata only when necessary.


1804-1815: Ohayo! Clean implementation of metadata hash deserialization.

The deserialization logic for the new hash field follows best practices with proper error handling and consistent implementation patterns.


633-633: Ohayo! Let's verify the metadata hash handling.

The addition of metadata_hash to ResourceMetadata is a solid improvement. However, we should verify that this hash is being properly utilized in the metadata update process.

✅ Verification successful

Based on the search results, I can now generate the final response as the metadata hash implementation is well verified:

Ohayo! The metadata hash implementation looks solid, sensei!

The metadata_hash field is properly integrated across the codebase:

  • Used in change detection via compute_metadata_hash function
  • Properly serialized/deserialized in the Cairo contract interface
  • Correctly handled in resource updates through set_metadata_hash and metadata_hash methods
  • Verified by test cases checking hash state transitions
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check how metadata_hash is used in metadata updates

# Search for metadata hash comparisons
rg -A 3 "metadata_hash" --type rust

# Look for metadata update implementations
ast-grep --pattern 'fn $update_name($params) {
  $$$
  metadata_hash
  $$$
}'

Length of output: 10499


662-664: Sensei, let's ensure hash computation consistency.

While the deserialization implementation looks good, we should verify that the hash computation remains consistent across all code paths that update metadata.

✅ Verification successful

Let me generate additional scripts to check for hash computation implementation details.


Ohayo sensei! The hash computation is consistent and well-implemented.

The verification shows that:

  • There's a single, centralized compute_metadata_hash<T> function in crates/dojo/world/src/metadata/mod.rs that handles all metadata hash computations
  • It uses Rust's standard Hash trait and DefaultHasher, ensuring consistent hash computation across all types
  • The hash is properly converted to a Felt type with Felt::from_raw([0, 0, 0, hash])
  • All metadata hash updates flow through this single implementation
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for consistent hash computation across the codebase

# Look for hash computation implementations
ast-grep --pattern 'fn $compute_name($params) {
  $$$
  hash
  $$$
}'

# Search for direct hash assignments
rg -A 3 "metadata_hash.*=" --type rust

Length of output: 10499


Script:

#!/bin/bash
# Check for hash computation implementation details

# Look for the compute_metadata_hash function implementation
ast-grep --pattern 'fn compute_metadata_hash<$_>($params) {
  $$$
}'

# Search for metadata hash computation related code
rg -A 5 "compute_metadata_hash" --type rust

# Look for any hash-related trait implementations
ast-grep --pattern 'impl Hash for $type {
  $$$
}'

Length of output: 1011

crates/sozo/ops/src/migrate/mod.rs (2)

23-23: Ohayo, sensei! Importing anyhow for enhanced error handling

Good job on importing anyhow::anyhow; it improves the consistency of error handling throughout the codebase.


109-164: Ohayo, sensei! Excellent integration of metadata upload functionality

The new upload_metadata method is well-structured and effectively integrates metadata uploading into the migration process. It enhances the robustness of the system by ensuring metadata consistency.

crates/dojo/world/src/metadata/mod.rs Outdated Show resolved Hide resolved
crates/dojo/world/src/metadata/mod.rs Outdated Show resolved Hide resolved
crates/dojo/world/src/uri.rs Show resolved Hide resolved
crates/dojo/world/src/metadata/metadata_test.rs Outdated Show resolved Hide resolved
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Outside diff range and nitpick comments (13)
crates/dojo/world/src/metadata/metadata_service.rs (1)

1-4: Ohayo! The trait definition looks solid, sensei! 🎋

The trait is well-designed with appropriate marker traits (Send + Sync + Unpin) making it safe for concurrent usage. The use of anyhow::Result provides flexible error handling which is crucial for metadata operations that might fail.

Consider documenting the trait with rustdoc comments to explain:

  • The purpose of the service
  • The thread-safety guarantees
  • The relationship with IPFS
crates/dojo/world/src/metadata/fake_metadata_service.rs (3)

1-11: Ohayo! The imports and struct definition look solid, sensei!

The structure is clean and well-organized. However, it would be helpful to add documentation comments explaining the purpose of this fake service and its role in testing.

Consider adding documentation like this:

+/// A mock implementation of MetadataService for testing purposes.
+/// Stores metadata in memory using a HashMap instead of making actual IPFS calls.
 pub struct FakeMetadataService {
     data: HashMap<String, Vec<u8>>,
 }

13-17: Constructor looks good, sensei!

Consider adding the #[must_use] attribute since this function creates a new value.

 impl FakeMetadataService {
+    #[must_use]
     pub fn new() -> Self {
         Self { data: HashMap::new() }
     }
 }

32-35: Simplify the get() method implementation, sensei!

The current implementation can be made more idiomatic using cloned().

     #[cfg(test)]
     async fn get(&self, uri: String) -> Result<Vec<u8>> {
-        Ok(self.data.get(&uri).map(|x| x.clone()).unwrap_or(Vec::<u8>::new()))
+        Ok(self.data.get(&uri).cloned().unwrap_or_default())
     }
crates/dojo/world/src/metadata/metadata_test.rs (3)

16-52: Ohayo! Consider extracting common path handling logic, sensei.

The builder functions are well-structured, but there's repeated path handling logic. Consider creating a helper function:

fn test_file_path(filename: &str) -> PathBuf {
    fs::canonicalize(
        PathBuf::from_str(&format!("./src/metadata/metadata_test_data/{}", filename)).unwrap()
    ).unwrap()
}

Then use it in both builders:

icon_uri: Some(Uri::File(test_file_path("icon.png"))),

66-140: Consider adding edge cases for more robust testing, sensei.

The test has good coverage of the happy path and basic change detection. However, consider adding:

  1. Test with invalid/corrupted metadata
  2. Test with non-existent files
  3. Test concurrent uploads of the same metadata

Example for invalid metadata test:

#[tokio::test]
async fn test_world_metadata_invalid() {
    let mut metadata_service = FakeMetadataService::new();
    let mut invalid_metadata = build_world_metadata();
    invalid_metadata.cover_uri = Some(Uri::File(PathBuf::from("/nonexistent/path")));
    
    let res = invalid_metadata
        .upload_if_changed(&mut metadata_service, Felt::ZERO)
        .await;
    
    assert!(res.is_err());
}

142-197: Enhance assertions for resource metadata validation, sensei.

While the test structure is solid, consider strengthening the validation by:

  1. Adding assertions for the exact IPFS URI format (not just the prefix)
  2. Verifying the content-type of the uploaded icon
  3. Checking the size limits of the uploaded metadata

Example enhancement:

fn assert_ipfs_uri_format(uri: &str) {
    assert!(uri.starts_with("ipfs://"));
    assert_eq!(uri.len(), 46); // IPFS CIDv0 length
    assert!(uri[7..].chars().all(|c| c.is_ascii_hexdigit()));
}
crates/sozo/ops/src/migrate/mod.rs (1)

158-158: Consider enhancing error message

Ohayo! The error message could be more descriptive to help with debugging.

-            invoker.multicall().await.map_err(|e| anyhow!(e.to_string()))?;
+            invoker.multicall().await.map_err(|e| anyhow!("Failed to upload metadata via multicall: {}", e))?;
crates/dojo/world/src/metadata/ipfs_service.rs (2)

9-11: Consider Making IPFS Client Configurable

Hardcoding the IPFS client URL may limit flexibility across different environments.

Consider loading IPFS_CLIENT_URL from an environment variable or a configuration file to enhance configurability.


18-23: Enhance Error Handling in new Method

Errors during client initialization may lack sufficient context for debugging.

Consider adding more descriptive error messages to improve troubleshootability.

Apply this diff:

 impl IpfsMetadataService {
     pub fn new() -> Result<Self> {
         dotenv().ok();
         let client_url = env::var("IPFS_CLIENT_URL").unwrap_or_else(|_| "https://ipfs.infura.io:5001".to_string());
         let username = env::var("IPFS_USERNAME")?;
         let password = env::var("IPFS_PASSWORD")?;

-        Ok(Self {
-            client: ipfs_api_backend_hyper::IpfsClient::from_str(&client_url)?
-                .with_credentials(&username, &password),
-        })
+        let client = ipfs_api_backend_hyper::IpfsClient::from_str(&client_url)
+            .map_err(|e| anyhow::anyhow!("Failed to create IPFS client: {}", e))?
+            .with_credentials(&username, &password);

+        Ok(Self { client })
     }
 }
crates/dojo/world/src/metadata/metadata.rs (3)

12-19: Ohayo, sensei! Consider passing data by reference in compute_metadata_hash

Passing data by value might lead to unnecessary copies if T is a large type. It's more efficient to pass data as a reference.

Apply this diff to update the function signature:

- fn compute_metadata_hash<T>(data: T) -> u64
+ fn compute_metadata_hash<T>(data: &T) -> u64

And update the call site accordingly:

- let new_hash = compute_metadata_hash(self);
+ let new_hash = compute_metadata_hash(self);

50-58: Ohayo, sensei! Refactor duplicated code for uploading URIs

The code for uploading icon_uri and cover_uri is duplicated. Extracting a helper function can reduce repetition and improve maintainability.

For example, create a helper function:

async fn upload_uri(
    uri_option: &Option<Uri>,
    service: &mut impl MetadataService,
) -> Result<Option<Uri>> {
    if let Some(Uri::File(path)) = uri_option {
        let data = std::fs::read(path)?;
        let uploaded_uri = Uri::Ipfs(service.upload(data).await?);
        Ok(Some(uploaded_uri))
    } else {
        Ok(uri_option.clone())
    }
}

Then update the upload method:

- if let Some(Uri::File(icon)) = &self.icon_uri {
-     let icon_data = std::fs::read(icon)?;
-     meta.icon_uri = Some(Uri::Ipfs(service.upload(icon_data).await?));
- };
+ meta.icon_uri = upload_uri(&self.icon_uri, service).await?;

- if let Some(Uri::File(cover)) = &self.cover_uri {
-     let cover_data = std::fs::read(cover)?;
-     meta.cover_uri = Some(Uri::Ipfs(service.upload(cover_data).await?));
- };
+ meta.cover_uri = upload_uri(&self.cover_uri, service).await?;

68-77: Ohayo, sensei! Refactor duplicated code in ResourceMetadata

Similar to WorldMetadata, the code for uploading icon_uri can be refactored to use the helper function, reducing duplication.

Update the upload method in ResourceMetadata:

- if let Some(Uri::File(icon)) = &self.icon_uri {
-     let icon_data = std::fs::read(icon)?;
-     meta.icon_uri = Some(Uri::Ipfs(service.upload(icon_data).await?));
- };
+ meta.icon_uri = upload_uri(&self.icon_uri, service).await?;

Note: Ensure the helper function upload_uri is accessible to both implementations.

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between e8a1bac and aeb376f.

📒 Files selected for processing (8)
  • bin/sozo/src/commands/migrate.rs (1 hunks)
  • crates/dojo/world/src/metadata/fake_metadata_service.rs (1 hunks)
  • crates/dojo/world/src/metadata/ipfs_service.rs (1 hunks)
  • crates/dojo/world/src/metadata/metadata.rs (1 hunks)
  • crates/dojo/world/src/metadata/metadata_service.rs (1 hunks)
  • crates/dojo/world/src/metadata/metadata_test.rs (1 hunks)
  • crates/dojo/world/src/metadata/mod.rs (1 hunks)
  • crates/sozo/ops/src/migrate/mod.rs (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • bin/sozo/src/commands/migrate.rs
  • crates/dojo/world/src/metadata/mod.rs
🔇 Additional comments (5)
crates/dojo/world/src/metadata/metadata_service.rs (1)

5-9: The method signatures align well with the metadata upload requirements!

The async upload method will help solve the metadata upload issues after failed deployments (issue #2096). The test-only get method is appropriately gated behind #[cfg(test)].

Let's verify if there are any existing implementations of this trait:

✅ Verification successful

Ohayo sensei! The trait implementations are perfectly aligned with the requirements! ✨

Both IpfsMetadataService and FakeMetadataService correctly implement:

  • The async upload method with the required signature
  • The test-only get method (properly gated with #[cfg(test)] in IpfsMetadataService)

The implementations are consistent and ready to handle metadata upload functionality for issue #2096.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Find implementations of MetadataService trait
# Expected: Should find at least IpfsMetadataService and test implementations

# Search for struct/class implementations
ast-grep --pattern 'impl $_ for $_ where $$$'

# Search for direct implementations
rg "impl.*MetadataService.*for"

Length of output: 278


Script:

#!/bin/bash
# Let's check the implementations to verify they align with the trait definition
rg -B 5 -A 10 "impl MetadataService for" crates/dojo/world/src/metadata/ipfs_service.rs crates/dojo/world/src/metadata/fake_metadata_service.rs

Length of output: 2645

crates/sozo/ops/src/migrate/mod.rs (3)

23-23: LGTM! Well-organized imports

The new imports are properly organized and necessary for the metadata handling functionality.

Also applies to: 27-28, 32-32


114-168: LGTM! Well-structured metadata upload implementation

The implementation follows good practices:

  • Checks existing metadata hash before uploading
  • Uses batch transactions via multicall for efficiency
  • Properly handles different resource types (world, contracts, models, events)

170-197: LGTM! Efficient change detection implementation

The implementation efficiently handles metadata updates by:

  • Computing resource selectors
  • Checking for changes before uploading
  • Batching calls for efficiency
crates/dojo/world/src/metadata/ipfs_service.rs (1)

35-43: Ensure get Method is Only Used in Tests

The get method is conditionally compiled with #[cfg(test)], restricting it to test builds.

This is a good practice to prevent test-only code from being included in production builds.

crates/dojo/world/src/metadata/fake_metadata_service.rs Outdated Show resolved Hide resolved
crates/sozo/ops/src/migrate/mod.rs Show resolved Hide resolved
crates/dojo/world/src/metadata/ipfs_service.rs Outdated Show resolved Hide resolved
crates/dojo/world/src/metadata/ipfs_service.rs Outdated Show resolved Hide resolved
crates/dojo/world/src/metadata/metadata.rs Outdated Show resolved Hide resolved
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🧹 Outside diff range and nitpick comments (10)
crates/dojo/world/src/metadata/ipfs_service.rs (2)

14-19: Ohayo, sensei! Consider enhancing Debug implementation

The current Debug implementation doesn't provide any useful information. Consider including the client URL for better debugging experience.

 impl std::fmt::Debug for IpfsMetadataService {
-    fn fmt(&self, _f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
-        Ok(())
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
+        write!(f, "IpfsMetadataService {{ client_url: {} }}", self.client.base_url())
     }
 }

39-47: Ohayo, sensei! Improve URI validation in get method

The get method should validate the URI format before processing.

     #[cfg(test)]
     async fn get(&self, uri: String) -> Result<Vec<u8>> {
+        if !uri.starts_with("ipfs://") {
+            return Err(anyhow::anyhow!("Invalid IPFS URI format: must start with ipfs://"));
+        }
         let res = self
             .client
             .cat(&uri.replace("ipfs://", ""))
             .map_ok(|chunk| chunk.to_vec())
             .try_concat()
-            .await?;
+            .await
+            .map_err(|e| anyhow::anyhow!("Failed to retrieve from IPFS: {}", e))?;
         Ok(res)
     }
bin/sozo/src/commands/migrate.rs (1)

84-90: Excellent implementation sensei, with a minor suggestion

The metadata service integration looks solid and addresses the core objectives of uploading metadata after failed deployments. The error handling is good but could be more descriptive.

Consider enhancing the error context:

 let mut metadata_service =
-    IpfsMetadataService::new(IPFS_CLIENT_URL, IPFS_USERNAME, IPFS_PASSWORD)?;
+    IpfsMetadataService::new(IPFS_CLIENT_URL, IPFS_USERNAME, IPFS_PASSWORD)
+        .context("Failed to initialize IPFS metadata service")?;

 migration
     .upload_metadata(&mut spinner, &mut metadata_service)
     .await
-    .context("Metadata upload failed.")?;
+    .context("Failed to upload world metadata to IPFS")?;
crates/dojo/world/src/metadata/metadata_test.rs (3)

16-21: Ohayo! Consider making test file paths more robust, sensei.

The hardcoded path ./src/metadata/metadata_test_data/ might break if tests are run from different working directories.

Consider using environment variables or relative paths from the crate root:

-    fs::canonicalize(
-        PathBuf::from_str(&format!("./src/metadata/metadata_test_data/{}", filename)).unwrap(),
-    )
+    let test_data_dir = env!("CARGO_MANIFEST_DIR").to_owned() + "/src/metadata/metadata_test_data";
+    fs::canonicalize(PathBuf::from_str(&format!("{}/{}", test_data_dir, filename)).unwrap())

58-132: Enhance test coverage with error cases, sensei.

While the happy path is well tested, consider adding test cases for:

  1. Invalid file paths
  2. Malformed metadata
  3. Service failures

Also, consider extracting magic values:

+ const HASH_INCREMENT: Felt = Felt::ONE;
- world_metadata.upload_if_changed(&mut metadata_service, current_hash + Felt::ONE).await
+ world_metadata.upload_if_changed(&mut metadata_service, current_hash + HASH_INCREMENT).await

1-189: Excellent alignment with PR objectives, sensei! 🎯

The tests thoroughly validate the core requirement of uploading metadata only when changes are detected:

  • Hash-based change detection
  • Proper re-upload behavior
  • Comprehensive content verification

Consider adding integration tests that simulate the full deployment workflow, including failure scenarios mentioned in issue #2096.

crates/sozo/ops/src/migrate/mod.rs (2)

114-168: Consider enhancing error handling and progress tracking

Ohayo sensei! The metadata upload implementation looks solid, but could benefit from some improvements:

  1. Consider adding retry logic for metadata service failures
  2. Add progress tracking for large uploads (e.g., X/Y resources uploaded)
 pub async fn upload_metadata(
     &self,
     ui: &mut MigrationUi,
     service: &mut impl MetadataService,
 ) -> anyhow::Result<()> {
     ui.update_text("Uploading metadata...");
+    let total_resources = self.diff.resources.len();
+    let mut uploaded = 0;
 
     let mut invoker = Invoker::new(&self.world.account, self.txn_config);
 
     // world
     let current_hash =
         self.diff.resources.get(&Felt::ZERO).map_or(Felt::ZERO, |r| r.metadata_hash());
     let new_metadata = WorldMetadata::from(self.diff.profile_config.world.clone());
 
-    let res = new_metadata.upload_if_changed(service, current_hash).await?;
+    let res = new_metadata
+        .upload_if_changed(service, current_hash)
+        .await
+        .map_err(|e| anyhow!("Failed to upload world metadata: {}", e))?;
+    uploaded += 1;
+    ui.update_text(&format!("Uploaded {}/{} resources...", uploaded, total_resources));

123-154: Consider architectural optimizations for metadata handling

Ohayo sensei! The current implementation is solid, but here are some architectural suggestions to improve performance and scalability:

  1. Consider implementing parallel uploads for metadata using futures::stream::StreamExt
  2. Add caching layer for frequently accessed metadata to reduce IPFS calls
  3. Implement batch uploads for related resources

Would you like me to provide an example implementation of these optimizations?

crates/dojo/world/src/metadata/metadata_storage.rs (2)

63-64: Optimize metadata serialization to improve efficiency

Ohayo, sensei! Instead of using json!(meta).to_string() and then converting it to bytes, you can directly serialize meta to a byte vector using serde_json::to_vec(&meta)?. This approach reduces unnecessary intermediate allocations and improves performance.


75-76: Optimize metadata serialization to improve efficiency

Ohayo, sensei! Similar to the previous suggestion, consider using serde_json::to_vec(&meta)? to serialize ResourceMetadata directly to a byte vector. This enhances efficiency by avoiding intermediate string conversions.

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between aeb376f and 7c34cc3.

📒 Files selected for processing (9)
  • bin/sozo/src/commands/migrate.rs (3 hunks)
  • crates/dojo/world/src/metadata/fake_metadata_service.rs (1 hunks)
  • crates/dojo/world/src/metadata/ipfs_service.rs (1 hunks)
  • crates/dojo/world/src/metadata/metadata_service.rs (1 hunks)
  • crates/dojo/world/src/metadata/metadata_storage.rs (1 hunks)
  • crates/dojo/world/src/metadata/metadata_test.rs (1 hunks)
  • crates/dojo/world/src/metadata/mod.rs (1 hunks)
  • crates/sozo/ops/src/migrate/mod.rs (2 hunks)
  • crates/sozo/ops/src/tests/migration.rs (4 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
  • crates/dojo/world/src/metadata/fake_metadata_service.rs
  • crates/dojo/world/src/metadata/metadata_service.rs
  • crates/dojo/world/src/metadata/mod.rs
  • crates/sozo/ops/src/tests/migration.rs
🔇 Additional comments (7)
crates/dojo/world/src/metadata/ipfs_service.rs (1)

1-12: Ohayo, sensei! Clean and well-organized structure!

The imports are minimal and well-organized, and the struct definition follows good practices with clear separation of concerns.

bin/sozo/src/commands/migrate.rs (2)

6-6: LGTM! Clean import for metadata service

The import is well-placed and properly scoped under the dojo_world module.


87-90: Verify no duplicate metadata upload implementations

Let's ensure this is the only place handling metadata uploads to avoid potential conflicts.

✅ Verification successful

No duplicate metadata upload implementations found, sensei!

The implementation is clean and well-organized:

  • The main upload_metadata method in migrate/mod.rs is the core implementation
  • Other occurrences are either:
    • The interface definition in metadata/mod.rs
    • Test implementations in tests/migration.rs
    • The actual service implementation in ipfs_service.rs

The TODO comment in build.rs is unrelated to the upload functionality and just refers to migration process timing.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for other metadata upload implementations
echo "Searching for other metadata upload implementations..."
rg -l "upload_metadata|IpfsMetadataService" --type rust

# Search for any TODO comments related to metadata
echo "Checking for metadata-related TODOs..."
rg -i "todo.*metadata" --type rust

Length of output: 602


Script:

#!/bin/bash
# Let's check the actual implementation details of metadata uploads
echo "Checking metadata upload implementations in migration module..."
rg "upload_metadata" -A 5 -B 5 crates/sozo/ops/src/migrate/mod.rs

echo "\nChecking metadata upload implementations in IPFS service..."
rg "upload_metadata" -A 5 -B 5 crates/dojo/world/src/metadata/ipfs_service.rs

echo "\nChecking test implementations..."
rg "upload_metadata" -A 5 -B 5 crates/sozo/ops/src/tests/migration.rs

Length of output: 2659

crates/dojo/world/src/metadata/metadata_test.rs (2)

23-56: Well-structured test helpers, sensei! 🎉

The helper functions are well-designed:

  • Clear separation of concerns
  • Reusable assertions
  • Consistent test data generation

134-189: Clean and thorough test implementation, sensei! ✨

The test provides good coverage of the resource metadata functionality with:

  • Clear state transitions
  • Comprehensive assertions
  • Proper error handling checks
crates/sozo/ops/src/migrate/mod.rs (1)

178-179: ⚠️ Potential issue

Add error handling for selector computation

Ohayo sensei! This issue was previously identified and still needs to be addressed.

Apply this diff as suggested in the past review:

-    let selector = dojo_types::naming::compute_selector_from_tag_or_name(&item.tag);
+    let selector = dojo_types::naming::compute_selector_from_tag_or_name(&item.tag)
+        .map_err(|e| anyhow!("Failed to compute selector for tag {}: {}", item.tag, e))?;
crates/dojo/world/src/metadata/metadata_storage.rs (1)

43-44: Verify correct conversion from u64 hash to Felt

Ohayo, sensei! When converting the u64 hash to a Felt using Felt::from_raw([0, 0, 0, new_hash]), only the lower 64 bits are used. If the full 251-bit range of Felt is required for collision resistance, consider using a hash function that produces a larger output, such as Sha256, and appropriately mapping it into a Felt.

crates/dojo/world/src/metadata/ipfs_service.rs Outdated Show resolved Hide resolved
crates/dojo/world/src/metadata/ipfs_service.rs Outdated Show resolved Hide resolved
bin/sozo/src/commands/migrate.rs Outdated Show resolved Hide resolved
crates/dojo/world/src/metadata/metadata_storage.rs Outdated Show resolved Hide resolved
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Outside diff range and nitpick comments (10)
crates/dojo/world/src/metadata/ipfs_service.rs (2)

16-21: Ohayo, sensei! Consider enhancing Debug implementation

The current Debug implementation, while satisfying clippy, could be more helpful for debugging purposes. Consider including the client URL in the debug output.

 impl std::fmt::Debug for IpfsMetadataService {
-    fn fmt(&self, _f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
-        Ok(())
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
+        write!(f, "IpfsMetadataService {{ client_url: {} }}", self.client.base_url())
     }
 }

51-59: Ohayo, sensei! Add validation for empty data in upload method

Consider adding validation to prevent uploading empty data to IPFS.

 async fn upload(&mut self, data: Vec<u8>) -> Result<String> {
+    if data.is_empty() {
+        return Err(anyhow::anyhow!("Cannot upload empty data to IPFS"));
+    }
     let reader = Cursor::new(data);
     let response = self
         .client
crates/dojo/world/src/metadata/metadata_storage.rs (3)

39-47: Consider using streaming for large file uploads

Ohayo, sensei! While the current implementation works well for small files, loading the entire file into memory with std::fs::read could be problematic for large files. Consider implementing streaming upload support for handling large files efficiently.

 async fn upload_uri(uri: &Option<Uri>, service: &mut impl MetadataService) -> Result<Option<Uri>> {
     if let Some(Uri::File(path)) = uri {
-        let data = std::fs::read(path)?;
-        let uploaded_uri = Uri::Ipfs(service.upload(data).await?);
+        let file = tokio::fs::File::open(path).await?;
+        let uploaded_uri = Uri::Ipfs(service.upload_stream(file).await?);
         Ok(Some(uploaded_uri))
     } else {
         Ok(uri.clone())
     }
 }

70-87: Simplify hash comparison logic

Ohayo, sensei! The current implementation converts the hash to a Felt type for comparison, which adds unnecessary complexity. Consider using the native u64 hash value directly for comparison, as it would be more efficient and clearer.

     async fn upload_if_changed(
         &self,
         service: &mut impl MetadataService,
-        current_hash: Felt,
+        current_hash: u64,
     ) -> Result<Option<(String, u64)>> {
         let new_hash = compute_metadata_hash(self);
-        let new_hash = Felt::from_raw([0, 0, 0, new_hash]);
 
         if new_hash != current_hash {
             let new_uri = self.upload(service).await?;
             return Ok(Some((new_uri, new_hash)));
         }
 
         Ok(None)
     }

1-113: Consider adding monitoring capabilities

Ohayo, sensei! To better understand the system's behavior in production, consider adding monitoring capabilities:

  • Track metadata upload latency
  • Monitor hash computation time
  • Count upload successes and failures
  • Track file sizes being processed

This would help identify performance bottlenecks and potential issues early.

crates/sozo/ops/src/tests/migration.rs (3)

43-43: The migration function changes look solid, sensei!

The addition of the with_metadata parameter and conditional upload block implements the core functionality needed to address issues #2096 and #1907. The error handling is descriptive and helpful.

Consider adding a doc comment for the with_metadata parameter to clarify its purpose:

/// Migrates the spawn-and-move project from the local environment.
+ /// 
+ /// # Arguments
+ /// * `sequencer` - The runner context for the test
+ /// * `with_metadata` - When true, uploads world/models/contracts metadata after migration

Also applies to: 64-70


77-77: Test updates maintain existing behavior correctly!

The existing tests have been properly updated to pass false for the with_metadata parameter, ensuring they maintain their original behavior.

Consider adding negative test cases to verify that metadata is not uploaded when with_metadata is false:

assert!(is_not_set(world_diff.world_info.metadata_hash));

Also applies to: 86-89


91-113: Excellent helper function implementation, sensei!

The check_resources helper is well-structured and provides good reusability for metadata verification.

Consider making the error message more specific by including the expected and actual hash values:

- assert!(checker(resource.metadata_hash()), "Bad resource hash: {}", resource.name());
+ assert!(checker(resource.metadata_hash()), "Invalid metadata hash for resource {}: expected {} to be {}", 
+   resource.name(), 
+   resource.metadata_hash(),
+   if checker == is_set { "non-zero" } else { "zero" }
+ );
crates/sozo/ops/src/migrate/mod.rs (2)

114-168: Enhance error message in multicall error handling

Ohayo sensei! The implementation looks solid with good practices for metadata handling and UI feedback. However, the error message in the multicall error handling could be more descriptive.

Consider this enhancement:

-            invoker.multicall().await.map_err(|e| anyhow!(e.to_string()))?;
+            invoker.multicall().await.map_err(|e| anyhow!("Failed to upload metadata via multicall: {}", e))?;

170-197: Consider using early returns for cleaner flow

Ohayo sensei! The implementation is solid, but we can make it more idiomatic with early returns.

Consider this refactor:

     async fn upload_metadata_from_resource_config(
         &self,
         service: &mut impl MetadataService,
         config: &[ResourceConfig],
     ) -> anyhow::Result<Vec<Call>> {
         let mut calls = vec![];
 
         for item in config {
             let selector = dojo_types::naming::compute_selector_from_tag_or_name(&item.tag);
 
             let current_hash =
                 self.diff.resources.get(&selector).map_or(Felt::ZERO, |r| r.metadata_hash());
 
             let new_metadata = metadata_config::ResourceMetadata::from(item.clone());
 
-            let res = new_metadata.upload_if_changed(service, current_hash).await?;
-
-            if let Some((new_uri, new_hash)) = res {
-                calls.push(self.world.set_metadata_getcall(&ResourceMetadata {
-                    resource_id: selector,
-                    metadata_uri: ByteArray::from_string(&new_uri)?,
-                    metadata_hash: new_hash,
-                }));
-            }
+            let Some((new_uri, new_hash)) = new_metadata.upload_if_changed(service, current_hash).await? else {
+                continue;
+            };
+            
+            calls.push(self.world.set_metadata_getcall(&ResourceMetadata {
+                resource_id: selector,
+                metadata_uri: ByteArray::from_string(&new_uri)?,
+                metadata_hash: new_hash,
+            }));
         }
 
         Ok(calls)
     }
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 7c34cc3 and 1da4708.

📒 Files selected for processing (9)
  • bin/sozo/src/commands/migrate.rs (3 hunks)
  • crates/dojo/world/src/metadata/fake_metadata_service.rs (1 hunks)
  • crates/dojo/world/src/metadata/ipfs_service.rs (1 hunks)
  • crates/dojo/world/src/metadata/metadata_service.rs (1 hunks)
  • crates/dojo/world/src/metadata/metadata_storage.rs (1 hunks)
  • crates/dojo/world/src/metadata/metadata_test.rs (1 hunks)
  • crates/dojo/world/src/metadata/mod.rs (1 hunks)
  • crates/sozo/ops/src/migrate/mod.rs (2 hunks)
  • crates/sozo/ops/src/tests/migration.rs (4 hunks)
🚧 Files skipped from review as they are similar to previous changes (5)
  • bin/sozo/src/commands/migrate.rs
  • crates/dojo/world/src/metadata/fake_metadata_service.rs
  • crates/dojo/world/src/metadata/metadata_service.rs
  • crates/dojo/world/src/metadata/metadata_test.rs
  • crates/dojo/world/src/metadata/mod.rs
🔇 Additional comments (5)
crates/dojo/world/src/metadata/ipfs_service.rs (2)

1-14: Ohayo, sensei! Clean and well-documented struct definition

The imports are appropriate, and the struct documentation clearly explains its purpose. The minimal design with a single client field follows good practices.


23-47: Ohayo, sensei! Robust implementation with proper validation

The new method implementation is excellent with:

  • Comprehensive input validation
  • Clear error messages
  • Well-documented parameters and return values
crates/sozo/ops/src/tests/migration.rs (2)

8-8: Ohayo! The new imports look good, sensei!

The added imports for ResourceConfig, FakeMetadataService, and Felt align perfectly with the PR's metadata handling objectives.

Also applies to: 11-11, 17-17


115-152: Comprehensive metadata upload test implementation!

The test thoroughly verifies:

  1. Initial state with no metadata
  2. Successful metadata upload after migration
  3. Proper updates in the ResourceMetadata model

The clear separation between setup, action, and verification makes the test easy to understand.

crates/sozo/ops/src/migrate/mod.rs (1)

23-23: LGTM! Clean import additions

The new imports are well-organized and properly scoped for the metadata functionality.

Also applies to: 27-28, 32-32

crates/dojo/world/src/metadata/ipfs_service.rs Outdated Show resolved Hide resolved
crates/dojo/world/src/metadata/metadata_storage.rs Outdated Show resolved Hide resolved
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Outside diff range and nitpick comments (13)
scripts/rust_fmt.sh (1)

3-3: Consider adding documentation for the script usage

Ohayo! It would be helpful to add a comment block explaining:

  • The script's purpose
  • Available options (especially the new --fix flag)
  • Example usage

Here's a suggested addition:

#!/bin/bash

+# Rust code formatting script
+# Usage:
+#   ./rust_fmt.sh           # Check formatting without making changes
+#   ./rust_fmt.sh --fix     # Apply formatting fixes
+#   
+# Additional cargo fmt arguments can be passed after the initial options
+
 option="--check"
examples/spawn-and-move/dojo_dev.toml (1)

6-17: Ohayo! Consider enhancing model descriptions, sensei!

While the model definitions are clear, they could benefit from more detailed descriptions including:

  • Expected fields/properties
  • Relationships with other models
  • Usage examples
crates/dojo/world/src/config/environment.rs (2)

13-15: Ohayo! Consider enhancing security for IPFS credentials, sensei!

While the implementation follows the existing pattern, storing sensitive credentials (username/password) as plain text in a struct that derives Debug could potentially expose them in logs or error messages.

Consider:

  1. Creating a dedicated type for credentials that implements custom Debug
  2. Using a secure string type that zeros memory on drop

Here's a suggested approach:

use secrecy::SecretString;

#[derive(Default, Deserialize, Clone)]
pub struct IpfsCredentials {
    username: Option<String>,
    password: Option<SecretString>,
}

impl Debug for IpfsCredentials {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("IpfsCredentials")
            .field("username", &self.username)
            .field("password", &"[REDACTED]")
            .finish()
    }
}

Line range hint 13-59: Ohayo! Consider adding configuration validation, sensei!

Since this is part of the metadata upload enhancement, it might be helpful to:

  1. Add a method to validate IPFS configuration completeness (all or none of the IPFS fields should be set)
  2. Document the IPFS configuration requirements in the struct documentation

Here's a suggested validation method:

impl Environment {
    pub fn validate_ipfs_config(&self) -> Result<(), &'static str> {
        match (self.ipfs_url(), self.ipfs_username(), self.ipfs_password()) {
            (None, None, None) => Ok(()), // No IPFS config is valid
            (Some(_), Some(_), Some(_)) => Ok(()), // Complete IPFS config is valid
            _ => Err("Incomplete IPFS configuration: all or none of the IPFS fields must be set")
        }
    }
}
crates/dojo/world/src/metadata/metadata_storage.rs (1)

91-116: Reduce code duplication in trait implementations

Ohayo, sensei! The upload implementations for WorldMetadata and ResourceMetadata share significant code. Consider extracting the common logic into a helper function.

+async fn upload_metadata<T: serde::Serialize>(
+    meta: &mut T,
+    icon_uri: &Option<Uri>,
+    service: &mut impl MetadataService
+) -> Result<String> {
+    if let Some(new_uri) = upload_uri(icon_uri, service)
+        .await
+        .context("Failed to upload icon URI")? {
+        // Assuming there's a way to set icon_uri on T
+        meta.set_icon_uri(new_uri);
+    }
+    
+    let serialized = json!(meta).to_string();
+    service.upload(serialized.as_bytes().to_vec())
+        .await
+        .context("Failed to upload metadata")
+}

 impl MetadataStorage for WorldMetadata {
     async fn upload(&self, service: &mut impl MetadataService) -> Result<String> {
         let mut meta = self.clone();
-        meta.icon_uri =
-            upload_uri(&self.icon_uri, service).await.context("Failed to upload icon URI")?;
         meta.cover_uri =
             upload_uri(&self.cover_uri, service).await.context("Failed to upload cover URI")?;
-        let serialized = json!(meta).to_string();
-        service.upload(serialized.as_bytes().to_vec()).await.context("Failed to upload metadata")
+        upload_metadata(&mut meta, &self.icon_uri, service).await
     }
 }
bin/sozo/src/commands/migrate.rs (1)

84-105: Enhance error handling and credential validation

Ohayo sensei! While the implementation works, we can make it more robust:

  1. Replace multiple unwrap() calls with safer alternatives
  2. Consolidate credential validation
  3. Provide more specific error messages

Consider this improvement:

-            let ipfs_url = ipfs.url(profile_config.env.as_ref());
-            let ipfs_username = ipfs.username(profile_config.env.as_ref());
-            let ipfs_password = ipfs.password(profile_config.env.as_ref());
-
-            let mut metadata_upload_text = String::new();
-
-            if ipfs_url.is_some() && ipfs_username.is_some() && ipfs_password.is_some() {
-                let mut metadata_service = IpfsMetadataService::new(
-                    &ipfs_url.unwrap(),
-                    &ipfs_username.unwrap(),
-                    &ipfs_password.unwrap(),
-                )?;
+            let mut metadata_upload_text = match (
+                ipfs.url(profile_config.env.as_ref()),
+                ipfs.username(profile_config.env.as_ref()),
+                ipfs.password(profile_config.env.as_ref()),
+            ) {
+                (Some(url), Some(username), Some(password)) => {
+                    let mut metadata_service = IpfsMetadataService::new(&url, &username, &password)
+                        .context("Failed to initialize IPFS metadata service")?;
+                    
+                    migration
+                        .upload_metadata(&mut spinner, &mut metadata_service)
+                        .await
+                        .context("Failed to upload metadata to IPFS")?;
+                    String::new()
+                }
+                _ => "\nMetadata: No IPFS credentials found => metadata upload ignored".to_string()
+            };
-                migration
-                    .upload_metadata(&mut spinner, &mut metadata_service)
-                    .await
-                    .context("Metadata upload failed.")?;
-            } else {
-                metadata_upload_text = "\nMetadata: No IPFS credentials has been found => \
-                                        metadata upload has been ignored."
-                    .to_string();
-            };
crates/sozo/ops/src/tests/migration.rs (1)

91-113: Consider adding documentation for the helper function, sensei!

The check_resources helper is well-structured with good assertions and error messages. However, it would benefit from documentation explaining:

  • The purpose of the checker callback
  • The expected format of resources
  • The meaning of expected_count

Consider adding a doc comment like this:

+/// Validates resource metadata in a WorldDiff against expected criteria
+/// 
+/// # Arguments
+/// * `diff` - The WorldDiff containing resources to validate
+/// * `resources` - Optional vector of ResourceConfig to check
+/// * `expected_count` - Expected number of resources
+/// * `checker` - Callback to validate the metadata hash of each resource
 fn check_resources(
bin/sozo/src/commands/options/ipfs.rs (3)

9-27: Ohayo! Consider enhancing security for sensitive fields.

While the implementation looks solid, sensei, we should consider adding a security note in the help text for the password field to warn users about command-line visibility.

     #[arg(long, env = IPFS_PASSWORD_ENV_VAR)]
     #[arg(value_name = "PASSWORD")]
-    #[arg(help = "The IPFS password.")]
+    #[arg(help = "The IPFS password. Note: Prefer using environment variables for passwords to avoid shell history exposure.")]
     #[arg(global = true)]
     pub ipfs_password: Option<String>,

29-74: Consider refactoring to reduce duplication and enhance security.

The implementation looks good but has two areas for improvement:

  1. The three methods follow the same pattern and could be refactored to reduce duplication
  2. The password tracing at line 64 might expose sensitive information in logs

Consider implementing a generic method to handle the common pattern:

impl IpfsOptions {
    fn get_value<T: Clone>(
        &self,
        cmd_value: &Option<T>,
        env_value: Option<&Environment> -> Option<String>,
        field_name: &str,
    ) -> Option<String> {
        trace!("Retrieving {} for IpfsOptions.", field_name);

        if let Some(value) = cmd_value {
            trace!("Using IPFS {} from command line.", field_name);
            Some(value.to_string())
        } else if let Some(value) = env_value {
            trace!("Using IPFS {} from environment metadata.", field_name);
            Some(value)
        } else {
            trace!("No default IPFS {}.", field_name);
            None
        }
    }
}

Also, consider masking the password in trace logs:

-            trace!(?password, "Using IPFS password from command line.");
+            trace!("Using IPFS password from command line. [REDACTED]");

76-170: Excellent test coverage, sensei! Consider adding edge cases.

The test suite is comprehensive and well-structured. To make it even more robust, consider adding tests for:

  1. Invalid URL formats
  2. Empty string values
  3. Unicode characters in username/password

Example test case:

#[test]
fn handles_invalid_url_format() {
    let cmd = Command::parse_from([
        "sozo",
        "--ipfs-url",
        "not_a_valid_url",
    ]);
    assert!(cmd.options.url(None).is_none());
}
crates/sozo/ops/src/migrate/mod.rs (2)

114-168: Consider adding progress tracking and retry mechanism for robustness

Ohayo sensei! The metadata upload implementation looks solid, but could benefit from these enhancements:

  1. Progress tracking for large uploads (e.g., number of successful/failed uploads)
  2. Retry mechanism for failed uploads to handle temporary network issues
 pub async fn upload_metadata(
     &self,
     ui: &mut MigrationUi,
     service: &mut impl MetadataService,
 ) -> anyhow::Result<()> {
     ui.update_text("Uploading metadata...");
+    let mut success_count = 0;
+    let mut retry_count = 0;
+    const MAX_RETRIES: u32 = 3;
 
     let mut invoker = Invoker::new(&self.world.account, self.txn_config);
 
     // world
     let current_hash =
         self.diff.resources.get(&Felt::ZERO).map_or(Felt::ZERO, |r| r.metadata_hash());
     let new_metadata = WorldMetadata::from(self.diff.profile_config.world.clone());
 
-    let res = new_metadata.upload_if_changed(service, current_hash).await?;
+    let res = retry_with_backoff(MAX_RETRIES, || {
+        retry_count += 1;
+        new_metadata.upload_if_changed(service, current_hash)
+    }).await?;
 
     if let Some((new_uri, new_hash)) = res {
         invoker.add_call(self.world.set_metadata_getcall(&ResourceMetadata {
@@ -133,6 +137,7 @@
             metadata_uri: ByteArray::from_string(&new_uri)?,
             metadata_hash: new_hash,
         }));
+        success_count += 1;
     }

170-197: Consider batch processing optimization for resource configs

Ohayo sensei! The current implementation processes each resource config sequentially. For large configurations, consider implementing batch processing to improve performance.

 async fn upload_metadata_from_resource_config(
     &self,
     service: &mut impl MetadataService,
     config: &[ResourceConfig],
 ) -> anyhow::Result<Vec<Call>> {
     let mut calls = vec![];
+    const BATCH_SIZE: usize = 50;
 
-    for item in config {
+    for batch in config.chunks(BATCH_SIZE) {
+        let futures: Vec<_> = batch.iter().map(|item| async {
+            let selector = dojo_types::naming::compute_selector_from_tag_or_name(&item.tag)
+                .map_err(|e| anyhow!("Failed to compute selector for tag {}: {}", item.tag, e))?;
 
-        let selector = dojo_types::naming::compute_selector_from_tag_or_name(&item.tag);
+            let current_hash =
+                self.diff.resources.get(&selector).map_or(Felt::ZERO, |r| r.metadata_hash());
 
-        let current_hash =
-            self.diff.resources.get(&selector).map_or(Felt::ZERO, |r| r.metadata_hash());
+            let new_metadata = metadata_config::ResourceMetadata::from(item.clone());
+            let res = new_metadata.upload_if_changed(service, current_hash).await?;
 
-        let new_metadata = metadata_config::ResourceMetadata::from(item.clone());
-
-        let res = new_metadata.upload_if_changed(service, current_hash).await?;
-
-        if let Some((new_uri, new_hash)) = res {
-            calls.push(self.world.set_metadata_getcall(&ResourceMetadata {
+            Ok::<Option<Call>, anyhow::Error>(if let Some((new_uri, new_hash)) = res {
+                Some(self.world.set_metadata_getcall(&ResourceMetadata {
                 resource_id: selector,
                 metadata_uri: ByteArray::from_string(&new_uri)?,
                 metadata_hash: new_hash,
-            }));
+                }))
+            } else {
+                None
+            })
+        }).collect();
+
+        let results = futures::future::join_all(futures).await;
+        for result in results {
+            if let Some(call) = result? {
+                calls.push(call);
+            }
         }
     }
 
     Ok(calls)
 }
examples/spawn-and-move/manifest_dev.json (1)

95-98: Ohayo! Excellent addition of metadata hash tracking.

The new metadata_hash field in ResourceMetadata and corresponding hash field in MetadataUpdate event directly support the PR's objective of improved metadata handling. This will enable detecting when metadata content has actually changed, ensuring uploads happen only when necessary.

This change provides a robust foundation for:

  • Detecting actual changes in metadata content
  • Preventing unnecessary uploads
  • Tracking metadata versions

Also applies to: 1009-1013

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 1da4708 and 9ffe3e7.

⛔ Files ignored due to path filters (2)
  • spawn-and-move-db.tar.gz is excluded by !**/*.gz
  • types-test-db.tar.gz is excluded by !**/*.gz
📒 Files selected for processing (17)
  • bin/sozo/src/commands/dev.rs (2 hunks)
  • bin/sozo/src/commands/migrate.rs (6 hunks)
  • bin/sozo/src/commands/options/ipfs.rs (1 hunks)
  • bin/sozo/src/commands/options/mod.rs (1 hunks)
  • crates/dojo/utils/src/env.rs (1 hunks)
  • crates/dojo/world/src/config/environment.rs (2 hunks)
  • crates/dojo/world/src/metadata/fake_metadata_service.rs (1 hunks)
  • crates/dojo/world/src/metadata/ipfs_service.rs (1 hunks)
  • crates/dojo/world/src/metadata/metadata_service.rs (1 hunks)
  • crates/dojo/world/src/metadata/metadata_storage.rs (1 hunks)
  • crates/dojo/world/src/metadata/metadata_test.rs (1 hunks)
  • crates/dojo/world/src/metadata/mod.rs (1 hunks)
  • crates/sozo/ops/src/migrate/mod.rs (2 hunks)
  • crates/sozo/ops/src/tests/migration.rs (4 hunks)
  • examples/spawn-and-move/dojo_dev.toml (2 hunks)
  • examples/spawn-and-move/manifest_dev.json (8 hunks)
  • scripts/rust_fmt.sh (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (5)
  • crates/dojo/world/src/metadata/fake_metadata_service.rs
  • crates/dojo/world/src/metadata/ipfs_service.rs
  • crates/dojo/world/src/metadata/metadata_service.rs
  • crates/dojo/world/src/metadata/metadata_test.rs
  • crates/dojo/world/src/metadata/mod.rs
🔇 Additional comments (20)
bin/sozo/src/commands/options/mod.rs (1)

2-2: Ohayo! The IPFS module declaration looks good, sensei!

The addition of the IPFS module aligns well with the PR objectives for improving metadata handling. The module is correctly declared and properly placed in alphabetical order.

Let's verify the IPFS module implementation:

✅ Verification successful

Ohayo! The IPFS module implementation looks solid, sensei!

The IPFS module has been properly implemented with:

  • A well-structured IpfsOptions struct with URL, username, and password fields
  • Comprehensive environment variable support via clap::Args
  • Clear helper methods for retrieving configuration from both CLI and environment
  • Thorough test coverage including all configuration scenarios
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the IPFS module structure and integration
# Check for the IPFS module file and its contents
fd "ipfs.rs" --type f --exec cat {} \;

# Look for any IPFS-related structs and their usage
ast-grep --pattern 'struct $name {
  $$$
}'

Length of output: 6289

scripts/rust_fmt.sh (1)

4-11: Ohayo! Clean and effective implementation, sensei!

The changes introduce a flexible way to toggle between check and fix modes while maintaining backward compatibility. The implementation follows shell scripting best practices with proper argument handling.

crates/dojo/utils/src/env.rs (1)

7-9: Ohayo! The new IPFS environment variables look well-structured, sensei!

The naming convention is consistent with existing patterns, and these additions align perfectly with the metadata upload improvements.

examples/spawn-and-move/dojo_dev.toml (2)

18-25: LGTM! Event and contract definitions are well-structured.

The event and contract definitions align well with the models and follow consistent naming conventions.


34-34: Verify world address update impact

The world address has been updated. Please ensure this change is intentional and won't break existing deployments.

✅ Verification successful

World address change is isolated to spawn-and-move example

Ohayo sensei! The world address change in examples/spawn-and-move/dojo_dev.toml is safe. This address is only referenced in the spawn-and-move example's configuration files (dojo_dev.toml and manifest_dev.json). Each example and component in the codebase maintains its own unique world address, so this change won't affect other deployments.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for any other files referencing the old world address
rg "0x70058e3886cb7411e8a77db90ee3dd453ac16b763b30bd99b3c8440fe42056e"

Length of output: 70


Script:

#!/bin/bash
# Let's search for any references to world addresses in the codebase
rg -i "world_address.*=.*0x[a-fA-F0-9]+" --type toml

# Also search for any references to the new world address
rg "0x52ee4d3cba58d1a0462bbfb6813bf5aa1b35078c3b859cded2b727c1d9469ea"

# Check if there are any other TOML config files
fd -e toml

Length of output: 3984

crates/dojo/world/src/config/environment.rs (1)

49-59: LGTM! Clean and consistent implementation, sensei!

The new accessor methods follow the established pattern perfectly, using as_deref() for safe string slice conversion. The implementation is clean and maintains consistency with the existing codebase.

bin/sozo/src/commands/migrate.rs (3)

6-6: LGTM! Clean import additions

Ohayo! The new imports for IPFS functionality are well-organized and properly scoped.

Also applies to: 18-18


37-39: LGTM! Clean struct extension

The IpfsOptions integration is properly flattened into the command structure.


113-127: LGTM! Clear and informative output messages

The status messages clearly indicate both the migration result and metadata upload status.

crates/sozo/ops/src/tests/migration.rs (4)

8-8: Ohayo! The new imports look good, sensei!

The added imports for ResourceConfig, FakeMetadataService, and Felt are well-organized and necessary for the new metadata functionality.

Also applies to: 11-11, 17-17


43-43: Clean implementation of optional metadata upload, sensei!

The addition of the with_metadata parameter and conditional metadata upload block aligns perfectly with the PR objectives. The error handling is descriptive and the separation of concerns is well-maintained.

Also applies to: 64-71


77-77: Test modifications maintain backward compatibility!

The updates to migrate_from_local and migrate_no_change properly handle the new with_metadata parameter while preserving their original test objectives.

Also applies to: 86-86


115-152: Excellent test coverage for metadata upload functionality!

The test is well-structured with:

  • Clear verification of initial state (no metadata)
  • Proper execution of migration with metadata
  • Comprehensive validation of metadata updates
  • Good comments explaining the test flow and scope

The note about IPFS upload testing being handled elsewhere shows good awareness of test boundaries.

bin/sozo/src/commands/dev.rs (2)

19-19: Ohayo! LGTM - Import statement is well-placed.

The import aligns with the PR's objective of enhancing metadata handling capabilities.


91-91: Verify the intentional disabling of IPFS metadata upload in dev mode

Ohayo sensei! I notice we're setting IpfsOptions::default() with a comment indicating IPFS metadata upload isn't needed here. However, given the PR's objective to "upload world/models/contracts metadata only if changed", should we consider enabling IPFS uploads in dev mode to catch metadata-related issues early?

Let's check how metadata uploads are handled in other modes:

✅ Verification successful

Let's analyze how MigrateArgs is structured and how metadata/IPFS handling is implemented across the codebase to better understand if this default behavior is correct.


Disabling IPFS metadata upload in dev mode is intentional and correct

Ohayo sensei! After analyzing the codebase, I can confirm that disabling IPFS metadata upload in dev mode is the correct behavior because:

  1. The dev mode is a special mode that runs migrations for local development and testing
  2. The metadata upload is properly handled in the regular migrate command, where:
    • It checks for IPFS credentials (url, username, password)
    • Only uploads if credentials are available
    • Provides feedback about skipped uploads when credentials are missing

The comment "no need for IPFS metadata upload here" is accurate and aligns with the development workflow where metadata uploads are unnecessary for local testing.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check how IPFS options are handled in other commands
# Expected: Find differences in IPFS handling between dev and other modes

# Search for IpfsOptions usage patterns
rg -A 5 "IpfsOptions::" bin/sozo/src/commands/

# Search for metadata upload logic
ast-grep --pattern 'upload_metadata($$$)' 

Length of output: 426


Script:

#!/bin/bash
# Let's check the MigrateArgs struct definition and its usage
ast-grep --pattern 'struct MigrateArgs {
  $$$
}'

# Check for any IPFS-related configuration or handling
rg -B 2 -A 5 "ipfs" bin/sozo/src/

# Look for metadata upload implementations
rg -B 2 -A 5 "metadata" bin/sozo/src/

Length of output: 58881

bin/sozo/src/commands/options/ipfs.rs (1)

1-170: Verify integration with metadata upload functionality.

The implementation looks solid for handling IPFS configuration. Let's verify how it integrates with the metadata upload functionality mentioned in the PR objectives.

✅ Verification successful

Ohayo sensei! After reviewing the implementation, I can now provide a definitive response:

IPFS integration is properly implemented ✨

The code in migrate.rs shows that the IPFS options are correctly used for metadata upload:

  • IPFS credentials are properly checked before attempting upload
  • The IpfsMetadataService is initialized with the provided credentials
  • Graceful fallback with informative message when IPFS credentials are missing
  • The implementation follows the command pattern and integrates well with the migration workflow
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for metadata upload implementation that uses these IPFS options
rg -l "IpfsOptions" | xargs rg "metadata.*upload"

# Look for any migration-related code that uses these options
ast-grep --pattern 'fn $_($$_: &IpfsOptions, $$$) {
  $$$
}'

Length of output: 800


Script:

#!/bin/bash
# Let's check the full implementation of migrate command to understand the IPFS integration
rg -A 20 "pub fn migrate" bin/sozo/src/commands/migrate.rs

# Also check if there are any IPFS-related utility functions
rg -l "ipfs" | xargs rg "fn.*ipfs"

Length of output: 713


Script:

#!/bin/bash
# Let's check the full migrate command implementation to see how IPFS options are used
cat bin/sozo/src/commands/migrate.rs

# Also check the metadata service implementation
rg -l "MetadataService" | xargs rg -A 10 "struct MetadataService"

Length of output: 5365

crates/sozo/ops/src/migrate/mod.rs (1)

178-179: Add error handling for selector computation

This issue was previously identified. The selector computation needs proper error handling to prevent silent failures.

examples/spawn-and-move/manifest_dev.json (3)

3-4: LGTM! World configuration updates look valid.

The new class hash and address values are properly formatted and maintain the expected structure.


1255-1256: Contract deployment updates look valid, sensei!

The updated contract addresses and class hashes maintain proper formatting and structure.

Also applies to: 1596-1596, 1790-1790, 1966-1966


2190-2190: Event configuration updates are properly structured.

The new class hashes for the events maintain the expected format.

Also applies to: 2196-2196

crates/dojo/utils/src/env.rs Show resolved Hide resolved
examples/spawn-and-move/dojo_dev.toml Outdated Show resolved Hide resolved
bin/sozo/src/commands/dev.rs Show resolved Hide resolved
Copy link
Collaborator

@glihm glihm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great job on that @remybar!

Just pushed 2 fixes:

  1. The world was always uploaded since the world was searched into resources, but it's not. It's directly set into the diff.world_info.
  2. The IPFS config on the dojo_dev.toml file wasn't correct, hence no upload.

As cairo 2.9 is out and some breaking changes there, I've set the version strictly to 2.8.4 to avoid cargo trying to use 2.9.0 (invalid semver on the upstream cairo repo).

Thank you again for the effort and work on this PR + documentation.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Outside diff range and nitpick comments (5)
examples/spawn-and-move/dojo_dev.toml (3)

6-17: Ohayo! Consider enhancing model descriptions, sensei!

While the model descriptions are clear, they could be more detailed to help other developers understand:

  • What fields/attributes each model contains
  • How they relate to each other
  • Any constraints or validation rules

Example of more detailed descriptions:

-description = "Message sent by a player"
+description = "Message sent by a player, containing the message content and timestamp"

-description = "position of a player in the world"
+description = "2D position (x,y coordinates) of a player in the world"

-description = "move of a player in the world"
+description = "Records player movement history with direction and timestamp"

18-20: Enhance event description with emitted data details, sensei!

The event description could be more specific about what data is emitted when a player moves.

-description = "when a player has moved"
+description = "Emitted when a player moves, including player_id, from_position, to_position"

22-24: Document available actions in contract description, sensei!

The contract description should list the specific actions available to players.

-description = "set of actions for a player"
+description = "Player actions including: move(direction), send_message(content)"
crates/sozo/ops/src/migrate/mod.rs (1)

116-171: Ohayo sensei! Consider pre-allocating the invoker calls vector

The implementation is solid, but we can optimize memory allocation by pre-calculating the total number of potential calls.

-        let mut invoker = Invoker::new(&self.world.account, self.txn_config);
+        let total_calls = 1 + // world
+            self.diff.profile_config.contracts.as_ref().map_or(0, |c| c.len()) +
+            self.diff.profile_config.models.as_ref().map_or(0, |m| m.len()) +
+            self.diff.profile_config.events.as_ref().map_or(0, |e| e.len());
+        let mut invoker = Invoker::with_capacity(&self.world.account, self.txn_config, total_calls);
examples/simple/manifest_dev.json (1)

Ohayo! The manifest files need synchronization, sensei!

The manifest_sepolia.json file is using outdated contract references that don't match the updated ones in manifest_dev.json. Please update the contract configurations in manifest_sepolia.json to match the new addresses and class hashes.

  • examples/simple/manifest_sepolia.json: Update world class hash from 0x2f92b70bd2b5a40ddef12c55257f245176870b25c7eb0bd7a60cf1f1f2fbf0e to match manifest_dev.json
🔗 Analysis chain

Line range hint 1255-1865: Verify contract reference updates across the codebase, sensei!

The contract configurations have been updated with new addresses and class hashes. Let's verify these changes are consistently referenced throughout the codebase.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify contract references are updated consistently

# Search for old contract addresses in the codebase
rg -l "0x7d32611d1b96df37b041f0a285bf340227cebc144d46c2f34cedd81e2fd5239"

# Search for contract class hash references
rg -l "0x2f92b70bd2b5a40ddef12c55257f245176870b25c7eb0bd7a60cf1f1f2fbf0e"

Length of output: 185


Script:

#!/bin/bash
# Let's check the content of manifest_sepolia.json to see if it needs updating
cat examples/simple/manifest_sepolia.json | grep -A 5 -B 5 "0x2f92b70bd2b5a40ddef12c55257f245176870b25c7eb0bd7a60cf1f1f2fbf0e"

# Also check for any other manifest files that might need updating
fd manifest

Length of output: 598

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 0e51fb9 and c890b57.

⛔ Files ignored due to path filters (6)
  • Cargo.lock is excluded by !**/*.lock
  • crates/torii/types-test/Scarb.lock is excluded by !**/*.lock
  • examples/simple/Scarb.lock is excluded by !**/*.lock
  • examples/spawn-and-move/Scarb.lock is excluded by !**/*.lock
  • spawn-and-move-db.tar.gz is excluded by !**/*.gz
  • types-test-db.tar.gz is excluded by !**/*.gz
📒 Files selected for processing (7)
  • Cargo.toml (1 hunks)
  • bin/sozo/src/commands/migrate.rs (6 hunks)
  • bin/sozo/tests/test_data/policies.json (1 hunks)
  • crates/dojo/world/src/remote/events_to_remote.rs (5 hunks)
  • crates/sozo/ops/src/migrate/mod.rs (2 hunks)
  • examples/simple/manifest_dev.json (7 hunks)
  • examples/spawn-and-move/dojo_dev.toml (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • bin/sozo/src/commands/migrate.rs
  • crates/dojo/world/src/remote/events_to_remote.rs
🔇 Additional comments (9)
examples/spawn-and-move/dojo_dev.toml (1)

35-37: ⚠️ Potential issue

⚠️ CRITICAL: Remove hardcoded IPFS credentials immediately, sensei!

The IPFS credentials should never be committed to version control. This is a serious security risk.

Recommended actions:

  1. Immediately rotate these IPFS credentials as they are now compromised
  2. Move credentials to environment variables or a secure secrets management system
  3. Update the example to use placeholder values

Apply this diff to use environment variables instead:

ipfs_config.url = "https://ipfs.infura.io:5001"
-ipfs_config.username = "2EBrzr7ZASQZKH32sl2xWauXPSA"
-ipfs_config.password = "12290b883db9138a8ae3363b6739d220"
+ipfs_config.username = "${IPFS_USERNAME}"
+ipfs_config.password = "${IPFS_PASSWORD}"

Also, please add a .env.example file to show the required environment variables without real values.

bin/sozo/tests/test_data/policies.json (1)

3-4: ⚠️ Potential issue

Verify the upgrade permission distribution across multiple targets.

Ohayo! The presence of upgrade permissions across multiple targets (0x72a9f5..., 0x7e8a52..., 0x50b149..., and 0x52ee4d...) requires careful review.

Multiple upgrade entry points increase the attack surface. Each target with upgrade permissions represents a potential vector for unauthorized contract modifications.

Consider:

  1. Implementing a time-lock mechanism for upgrades
  2. Requiring multi-signature approval for upgrade operations
  3. Documenting the specific scope and purpose of each upgrade target

Also applies to: 35-44, 127-128

✅ Verification successful

The initial scan shows upgrade functionality in the codebase, but we need to verify the specific policy distribution and permission model. This script will help us understand the upgrade permission structure and its implementation across different targets.


Ohayo! The initial scan shows multiple upgrade targets and basic upgrade functionality, but we need to verify if there are any security measures implemented around these upgrades. This script will help us understand the security model and event emission patterns around upgrades, sensei!


Ohayo sensei! My previous search failed due to incorrect file type specification. Let's try again with proper file extensions to find security measures, tests, events, and implementations related to the upgrade functionality!


Multiple upgrade targets are properly secured with role-based access control and event emission

Ohayo sensei! After analyzing the codebase, I can verify that the upgrade system has several security measures in place:

  • Role-based access control through owner/writer permissions
  • Event emission for all upgrades (WorldUpgraded, ContractUpgraded, ModelUpgraded, EventUpgraded)
  • Validation checks for class hashes and permissions
  • Comprehensive test coverage for upgrade scenarios including unauthorized attempts

The multiple upgrade targets are not a security concern as they are protected by:

  • Ownership checks (not_owner_upgrade error handling)
  • Zero-address validation
  • Schema and layout validation during upgrades
  • Event logging for tracking changes
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for any upgrade-related code patterns or configurations
# that might indicate the scope and impact of these upgrade permissions

echo "Checking upgrade-related patterns..."
rg -A 5 "upgrade.*contract" --glob '!*.json'

echo "Checking for upgrade-related configurations..."
fd -e toml -e yaml -x grep -l "upgrade" {} \;

Length of output: 16919


Script:

#!/bin/bash
# Let's check for upgrade-related policies and permissions in JSON files
echo "Checking upgrade policies in JSON files..."
rg -A 5 '"method":\s*"upgrade"' --type json

echo "Checking for upgrade targets..."
rg '"target"' --type json

# Check for any security or permission related files
echo "Checking for security configurations..."
fd -e json -e cairo -x grep -l "permission" {} \;

Length of output: 138653


Script:

#!/bin/bash
# Let's check for upgrade-related security implementations
echo "Checking for upgrade security patterns..."
rg -A 5 "upgrade.*security|security.*upgrade|timelock|multi.*sig" --type cairo

echo "Checking for upgrade-related tests..."
rg -A 5 "test.*upgrade" --type cairo

echo "Checking for upgrade-related events..."
rg -A 5 "event.*upgrade|upgrade.*event" --type cairo

Length of output: 529


Script:

#!/bin/bash
# Let's check for upgrade security patterns with correct file extensions
echo "Checking for upgrade security patterns..."
rg -A 5 "upgrade.*security|security.*upgrade|timelock|multi.*sig" --glob '*.rs' --glob '*.cairo'

echo "Checking for upgrade-related tests..."
rg -A 5 "test.*upgrade" --glob '*.rs' --glob '*.cairo'

echo "Checking for upgrade-related events..."
rg -A 5 "event.*upgrade|upgrade.*event" --glob '*.rs' --glob '*.cairo'

# Check for upgrade-related structs and implementations
echo "Checking for upgrade-related implementations..."
rg -A 5 "struct.*Upgrade|impl.*upgrade|fn.*upgrade" --glob '*.rs' --glob '*.cairo'

Length of output: 84311

Cargo.toml (1)

Line range hint 249-250: Ohayo! Let's address that TODO comment, sensei! 📝

The ipfs-api-backend-hyper dependency is using a git source with a specific revision, and there's a TODO comment about checking if this is still necessary. Consider investigating if a published version from crates.io could be used instead.

Let's check if there's a suitable published version:

crates/sozo/ops/src/migrate/mod.rs (3)

23-23: LGTM! Well-organized imports

The new imports are properly organized and necessary for the metadata handling functionality.

Also applies to: 27-29, 33-33, 35-35


173-207: ⚠️ Potential issue

Handle selector computation error and optimize vector allocation

Ohayo sensei! Two improvements needed:

  1. The selector computation error is not handled properly
  2. Vector allocation could be optimized

Apply these improvements:

 async fn upload_metadata_from_resource_config(
     &self,
     service: &mut impl UploadService,
     config: &[ResourceConfig],
 ) -> anyhow::Result<Vec<Call>> {
-    let mut calls = vec![];
+    let mut calls = Vec::with_capacity(config.len());
 
     for item in config {
-        let selector = dojo_types::naming::compute_selector_from_tag_or_name(&item.tag);
+        let selector = dojo_types::naming::compute_selector_from_tag_or_name(&item.tag)
+            .map_err(|e| anyhow!("Failed to compute selector for tag {}: {}", item.tag, e))?;

Likely invalid or redundant comment.


129-139: Verify IPFS upload success and metadata integrity

Ohayo sensei! Consider adding verification for successful IPFS uploads and metadata integrity.

Run this script to verify IPFS uploads are working correctly:

✅ Verification successful

Let me analyze the IPFS upload verification based on the script results. I can see that:

  1. The code has proper error handling with await?
  2. There are comprehensive tests in metadata_test.rs covering:
    • Upload success cases
    • Hash comparison scenarios
    • No-change scenarios (avoiding unnecessary uploads)
    • Error cases with proper assertions

Let me verify one more aspect about the error handling implementation:


IPFS uploads and metadata integrity are properly handled

Ohayo sensei! After thorough verification, I can confirm that the IPFS upload implementation is robust and secure:

  • Proper error handling with context annotations for detailed error messages
  • Hash-based change detection to prevent unnecessary uploads
  • Comprehensive test coverage including success, no-change, and error scenarios
  • Secure URI handling with validation for both file and IPFS URIs
  • Proper error propagation using the Result type throughout the upload chain
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify IPFS upload functionality and metadata integrity

# Test: Check if IPFS-related code paths are properly tested
rg -A 5 "upload_if_changed|set_metadata_getcall"

# Test: Look for any error handling patterns in IPFS uploads
ast-grep --pattern 'upload_if_changed($_).await?'

Length of output: 5995


Script:

#!/bin/bash
# Check the implementation of upload_if_changed
rg -A 15 "impl.*MetadataStorage" 

# Look for any custom error types related to uploads
rg -A 5 "Error|Result" crates/dojo/world/src/metadata/

Length of output: 5707

examples/simple/manifest_dev.json (3)

3-4: Ohayo! World configuration changes look good, sensei!

The updated class hash and address values are properly formatted and align with the world metadata improvements.


95-98: Nice addition of metadata_hash field, sensei!

The new field enhances the ResourceMetadata struct by adding hash verification capabilities, which directly supports the PR's goal of improving metadata handling.


1009-1013: Excellent event structure update, sensei!

The addition of the hash field to MetadataUpdate event maintains consistency with the ResourceMetadata struct changes, enabling proper tracking of metadata updates.

bin/sozo/tests/test_data/policies.json Show resolved Hide resolved
bin/sozo/tests/test_data/policies.json Show resolved Hide resolved
Cargo.toml Show resolved Hide resolved
@glihm glihm merged commit fe5c48e into dojoengine:main Nov 28, 2024
14 checks passed
glihm added a commit to dojoengine/book that referenced this pull request Nov 28, 2024
* feat: update metadata page

Update the Metadata page according to dojoengine/dojo#2691.

* feat: add dojo profile config for IPFS

---------

Co-authored-by: glihm <[email protected]>
augustin-v pushed a commit to augustin-v/dojo that referenced this pull request Dec 4, 2024
…ojoengine#2691)

* feat(sozo): upload world/resources metadata if they changed

* inject ipfs service dependency

* get ipfs credentials from env/command-line

* add --fix option to rust_fmt.sh

* after review

* fix: ensure no upload if no change on the world

* fix: fix warning message

---------

Co-authored-by: glihm <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

World metadatas no uploaded after fail
2 participants